From 96687ac49b311ababf2d3eb7054b07cf9627883d Mon Sep 17 00:00:00 2001 From: Ihor Sviziev Date: Wed, 10 Oct 2018 10:39:59 +0300 Subject: [PATCH 001/254] Checkout - Fix JS error Cannot read property 'quoteData' of undefined --- app/code/Magento/Checkout/view/frontend/web/js/model/quote.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js b/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js index 2510d1aced3d3..3486a92736617 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/model/quote.js @@ -7,7 +7,8 @@ */ define([ 'ko', - 'underscore' + 'underscore', + 'domReady!' ], function (ko, _) { 'use strict'; From 990f8eaeb7719398a3d2ed96739c2ba8afd3815f Mon Sep 17 00:00:00 2001 From: Alexandr Voronoy Date: Sun, 11 Nov 2018 15:57:28 +0200 Subject: [PATCH 002/254] Fix area for image placeholder in graphql response. --- .../Resolver/Product/ProductImage/Url.php | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php index 3c19ce599a9b3..9d53336dec70f 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php @@ -13,6 +13,7 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\View\Asset\Repository as AssetRepository; /** * Returns product's image url @@ -23,14 +24,22 @@ class Url implements ResolverInterface * @var ImageFactory */ private $productImageFactory; + /** + * @var AssetRepository + */ + private $assetRepository; /** + * Url constructor. * @param ImageFactory $productImageFactory + * @param AssetRepository $assetRepository */ public function __construct( - ImageFactory $productImageFactory + ImageFactory $productImageFactory, + AssetRepository $assetRepository ) { $this->productImageFactory = $productImageFactory; + $this->assetRepository = $assetRepository; } /** @@ -55,8 +64,7 @@ public function resolve( $product = $value['model']; $imagePath = $product->getData($value['image_type']); - $imageUrl = $this->getImageUrl($value['image_type'], $imagePath); - return $imageUrl; + return $this->getImageUrl($value['image_type'], $imagePath); } /** @@ -71,7 +79,15 @@ private function getImageUrl(string $imageType, ?string $imagePath): string $image = $this->productImageFactory->create(); $image->setDestinationSubdir($imageType) ->setBaseFile($imagePath); - $imageUrl = $image->getUrl(); + + $imageUrl = $image->isBaseFilePlaceholder() + ? $this->assetRepository->createAsset( + "Magento_Catalog::images/product/placeholder/{$imageType}.jpg", + ['area' => \Magento\Framework\App\Area::AREA_FRONTEND] + ) + ->getUrl() + : $image->getUrl(); + return $imageUrl; } } From 8867a57e773be7c63dc52e81d1add09ef7fda1fa Mon Sep 17 00:00:00 2001 From: Alexandr Voronoy Date: Sun, 18 Nov 2018 23:24:08 +0200 Subject: [PATCH 003/254] added logic if placeholder is overridden, unblock test. --- .../Resolver/Product/ProductImage/Url.php | 30 +++-- .../DataProvider/Image/Placeholder.php | 109 ++++++++++++++++++ .../GraphQl/Catalog/ProductImageTest.php | 1 - 3 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php index 9d53336dec70f..8b6275f2f3e0e 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php @@ -9,11 +9,11 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\ImageFactory; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image\Placeholder as PlaceholderProvider; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\View\Asset\Repository as AssetRepository; /** * Returns product's image url @@ -25,21 +25,22 @@ class Url implements ResolverInterface */ private $productImageFactory; /** - * @var AssetRepository + * @var PlaceholderProvider */ - private $assetRepository; + private $_placeholderProvider; /** * Url constructor. * @param ImageFactory $productImageFactory - * @param AssetRepository $assetRepository + * @param PlaceholderProvider $placeholderProvider */ public function __construct( ImageFactory $productImageFactory, - AssetRepository $assetRepository + PlaceholderProvider $placeholderProvider + ) { $this->productImageFactory = $productImageFactory; - $this->assetRepository = $assetRepository; + $this->_placeholderProvider = $placeholderProvider; } /** @@ -68,11 +69,12 @@ public function resolve( } /** - * Get image url + * Get image URL * * @param string $imageType - * @param string|null $imagePath Null if image is not set + * @param string|null $imagePath * @return string + * @throws \Exception */ private function getImageUrl(string $imageType, ?string $imagePath): string { @@ -80,14 +82,10 @@ private function getImageUrl(string $imageType, ?string $imagePath): string $image->setDestinationSubdir($imageType) ->setBaseFile($imagePath); - $imageUrl = $image->isBaseFilePlaceholder() - ? $this->assetRepository->createAsset( - "Magento_Catalog::images/product/placeholder/{$imageType}.jpg", - ['area' => \Magento\Framework\App\Area::AREA_FRONTEND] - ) - ->getUrl() - : $image->getUrl(); + if ($image->isBaseFilePlaceholder()) { + return $this->_placeholderProvider->getPlaceholder($imageType); + } - return $imageUrl; + return $image->getUrl(); } } 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 new file mode 100644 index 0000000000000..d9691a079a328 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder.php @@ -0,0 +1,109 @@ +_placeholderFactory = $placeholderFactory; + $this->_assetRepository = $assetRepository; + $this->_scopeConfig = $scopeConfig; + $this->_storeManager = $storeManager; + $this->_themeProvider = $themeProvider; + } + + /** + * Get placeholder + * + * @param $imageType + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function getPlaceholder($imageType): string + { + $imageAsset = $this->_placeholderFactory->create(['type' => $imageType]); + + // check if placeholder defined in config + if ($imageAsset->getFilePath()) { + return $imageAsset->getUrl(); + } + + return $this->_assetRepository->createAsset( + "Magento_Catalog::images/product/placeholder/{$imageType}.jpg", + $this->getThemeData() + )->getUrl(); + } + + /** + * Get theme model + * + * @return mixed + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function getThemeData() + { + $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/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductImageTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductImageTest.php index b55c6c1d91460..bb9c67f6d753d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductImageTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductImageTest.php @@ -51,7 +51,6 @@ public function testProductWithBaseImage() */ public function testProductWithoutBaseImage() { - $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/239'); $productSku = 'simple'; $query = << Date: Sat, 24 Nov 2018 16:28:08 +0200 Subject: [PATCH 004/254] store_view_code-column-has-empty-values-in-csv-17784. Moved ->clear(); --- app/code/Magento/CatalogImportExport/Model/Export/Product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 345d323e6e129..236b7caef1570 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -948,8 +948,8 @@ protected function loadCollection(): array foreach ($collection as $itemId => $item) { $data[$itemId][$storeId] = $item; } + $collection->clear(); } - $collection->clear(); return $data; } From 0186fe65984df11ee4973363fb4644bf05f3fc57 Mon Sep 17 00:00:00 2001 From: DmytroPaidych Date: Mon, 24 Dec 2018 16:25:00 +0200 Subject: [PATCH 005/254] MAGETWO-97237: Delete Product Staging Update when the Product is used --- .../Test/Mftf/Page/AdminUrlRewriteEditPage.xml | 14 ++++++++++++++ .../Mftf/Section/AdminUrlRewriteEditSection.xml | 14 ++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml new file mode 100644 index 0000000000000..b43e0e05ad55d --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml @@ -0,0 +1,14 @@ + + + + + +
+ + diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml new file mode 100644 index 0000000000000..7095214e72c43 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml @@ -0,0 +1,14 @@ + + + + +
+ +
+
From 25dcef64717325b00d8eb93cde1dde7b06093c40 Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Wed, 26 Dec 2018 14:53:14 -0500 Subject: [PATCH 006/254] Correct max page size for elasticsearch5 engine This value can be changed but can never exceed the value set for `index.max_result_window` in elasticsearch. This looks like it was caught and fixed for the `elasticsearch` engine, but not for `elasticsearch5` --- app/code/Magento/Elasticsearch/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index 6a42e4b3c9fe2..8adf589a91c6c 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -417,7 +417,7 @@ 10000 - 2147483647 + 10000 From 54e53eaffe2d3c3d076fe03d13cdf58baf5648f6 Mon Sep 17 00:00:00 2001 From: DmytroPaidych Date: Thu, 27 Dec 2018 12:32:56 +0200 Subject: [PATCH 007/254] MAGETWO-97237: Delete Product Staging Update when the Product is used --- .../AdminCreateWidgetActionGroup.xml | 19 +++++++++++++++++++ .../Widget/Test/Mftf/Data/WidgetsData.xml | 5 +++++ .../Mftf/Section/AdminNewWidgetSection.xml | 4 +++- ...dminNewWidgetSelectProductPopupSection.xml | 15 +++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml index 7955f4ec29e55..616dfef57a263 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml @@ -58,4 +58,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml index 26864c60b6494..639e4edb6b08f 100644 --- a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml +++ b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml @@ -19,4 +19,9 @@ All Pages Main Content Area + + + Catalog Product Link + Product Link Block Template + diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml index 38b4df335ea83..3bde60891ae1d 100644 --- a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -7,7 +7,7 @@ --> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
@@ -17,6 +17,7 @@ + @@ -25,5 +26,6 @@ +
diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml new file mode 100644 index 0000000000000..0da67849f85c6 --- /dev/null +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml @@ -0,0 +1,15 @@ + + + + +
+ + +
+
From 6d5ee6380cd9038fc1ef3403031e550f44440524 Mon Sep 17 00:00:00 2001 From: NazarKlovanych Date: Thu, 27 Dec 2018 11:31:38 +0200 Subject: [PATCH 008/254] Fix for issue 19983 --- .../Magento/Ui/view/base/web/js/form/element/file-uploader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index 357571350a268..c6bf36f5cc867 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -16,7 +16,8 @@ define([ 'Magento_Ui/js/form/element/abstract', 'mage/backend/notification', 'mage/translate', - 'jquery/file-uploader' + 'jquery/file-uploader', + 'mage/adminhtml/tools' ], function ($, _, utils, uiAlert, validator, Element, notification, $t) { 'use strict'; From 188a3e66e7d9604942d7c36cfaec7dc5bae32710 Mon Sep 17 00:00:00 2001 From: NazarKlovanych Date: Thu, 27 Dec 2018 12:53:39 +0200 Subject: [PATCH 009/254] Fix save customer attribute image --- app/code/Magento/Eav/Model/Attribute/Data/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Attribute/Data/File.php b/app/code/Magento/Eav/Model/Attribute/Data/File.php index 1b2cac32598e1..b8ca34986111b 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/File.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/File.php @@ -146,7 +146,7 @@ protected function _validateByRules($value) return $this->_fileValidator->getMessages(); } - if (!empty($value['tmp_name']) && !is_uploaded_file($value['tmp_name'])) { + if (empty($value['tmp_name']) && !is_uploaded_file($value['tmp_name'])) { return [__('"%1" is not a valid file.', $label)]; } From a042993b2d608df6e050a5e9b62509ef08be985b Mon Sep 17 00:00:00 2001 From: Anton Evers Date: Wed, 2 Jan 2019 14:02:29 +0100 Subject: [PATCH 010/254] Make it possible to generate sales PDF's using the API --- .../base/templates/info/pdf/default.phtml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 app/code/Magento/Payment/view/base/templates/info/pdf/default.phtml diff --git a/app/code/Magento/Payment/view/base/templates/info/pdf/default.phtml b/app/code/Magento/Payment/view/base/templates/info/pdf/default.phtml new file mode 100644 index 0000000000000..7acac62f65d38 --- /dev/null +++ b/app/code/Magento/Payment/view/base/templates/info/pdf/default.phtml @@ -0,0 +1,23 @@ + +escapeHtml($block->getMethod()->getTitle()) ?>{{pdf_row_separator}} + +getSpecificInformation()):?> + $value):?> + escapeHtml($label) ?>: + escapeHtml(implode(' ', $block->getValueAsArray($value))) ?> + {{pdf_row_separator}} + + + +escapeHtml(implode('{{pdf_row_separator}}', $block->getChildPdfAsArray())) ?> From c3fdba54dfc023bd92c5823682dc370235a59262 Mon Sep 17 00:00:00 2001 From: Anton Evers Date: Thu, 3 Jan 2019 13:54:57 +0100 Subject: [PATCH 011/254] Make it possible to generate sales PDF's using the API --- .../base/templates/info/pdf/checkmo.phtml | 26 +++++++++++++++++++ .../templates/info/pdf/purchaseorder.phtml | 11 ++++++++ 2 files changed, 37 insertions(+) create mode 100644 app/code/Magento/OfflinePayments/view/base/templates/info/pdf/checkmo.phtml create mode 100644 app/code/Magento/OfflinePayments/view/base/templates/info/pdf/purchaseorder.phtml diff --git a/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/checkmo.phtml b/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/checkmo.phtml new file mode 100644 index 0000000000000..4d63577319d5b --- /dev/null +++ b/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/checkmo.phtml @@ -0,0 +1,26 @@ + +escapeHtml($block->getMethod()->getTitle()) ?> + {{pdf_row_separator}} +getInfo()->getAdditionalInformation()): ?> + {{pdf_row_separator}} + getPayableTo()): ?> + escapeHtml(__('Make Check payable to: %1', $block->getPayableTo())) ?> + {{pdf_row_separator}} + + getMailingAddress()): ?> + escapeHtml(__('Send Check to:')) ?> + {{pdf_row_separator}} + escapeHtml($block->getMailingAddress())) ?> + {{pdf_row_separator}} + + diff --git a/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/purchaseorder.phtml b/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/purchaseorder.phtml new file mode 100644 index 0000000000000..4a6ea1c00b21c --- /dev/null +++ b/app/code/Magento/OfflinePayments/view/base/templates/info/pdf/purchaseorder.phtml @@ -0,0 +1,11 @@ + +escapeHtml(__('Purchase Order Number: %1', $block->getInfo()->getPoNumber())) ?> + {{pdf_row_separator}} From 55cf2dcb970f3f069e3d59ad7986413f80bb6cdb Mon Sep 17 00:00:00 2001 From: Rajneesh Gupta Date: Wed, 2 Jan 2019 18:25:40 +0530 Subject: [PATCH 012/254] String validation for filename added and updated unit test case magento#13937. --- .../Sitemap/Block/Adminhtml/Edit/Form.php | 3 +- .../Controller/Adminhtml/Sitemap/Save.php | 90 +++++++-- .../Controller/Adminhtml/Sitemap/SaveTest.php | 173 ++++++++++++++---- 3 files changed, 211 insertions(+), 55 deletions(-) diff --git a/app/code/Magento/Sitemap/Block/Adminhtml/Edit/Form.php b/app/code/Magento/Sitemap/Block/Adminhtml/Edit/Form.php index 5e90cf6e12f6e..2093e4d7d5736 100644 --- a/app/code/Magento/Sitemap/Block/Adminhtml/Edit/Form.php +++ b/app/code/Magento/Sitemap/Block/Adminhtml/Edit/Form.php @@ -73,7 +73,8 @@ protected function _prepareForm() 'name' => 'sitemap_filename', 'required' => true, 'note' => __('example: sitemap.xml'), - 'value' => $model->getSitemapFilename() + 'value' => $model->getSitemapFilename(), + 'class' => 'validate-length maximum-length-32' ] ); diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php index 1e0d1cb248f00..23e1d2e786900 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php @@ -11,6 +11,61 @@ class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap { + /** + * Maximum length of sitemap filename + */ + const MAX_FILENAME_LENGTH = 32; + + /** + * @var $_stringValidator + */ + public $_stringValidator; + + /** + * @var $_pathValidator + */ + public $_pathValidator; + + /** + * @var $_sitemapHelper + */ + public $_sitemapHelper; + + /** + * @var $_filesystem + */ + public $_filesystem; + + /** + * @var $_sitemapFactory + */ + public $_sitemapFactory; + + /** + * Save constructor. + * @param Action\Context $context + * @param \Magento\Framework\Validator\StringLength $stringValidator + * @param \Magento\MediaStorage\Model\File\Validator\AvailablePath $pathValidator + * @param \Magento\Sitemap\Helper\Data $sitemapHelper + * @param \Magento\Framework\Filesystem $filesystem + * @param \Magento\Sitemap\Model\SitemapFactory $sitemapFactory + */ + public function __construct( + \Magento\Backend\App\Action\Context $context, + \Magento\Framework\Validator\StringLength $stringValidator, + \Magento\MediaStorage\Model\File\Validator\AvailablePath $pathValidator, + \Magento\Sitemap\Helper\Data $sitemapHelper, + \Magento\Framework\Filesystem $filesystem, + \Magento\Sitemap\Model\SitemapFactory $sitemapFactory + ) { + parent::__construct($context); + $this->_stringValidator = $stringValidator; + $this->_pathValidator = $pathValidator; + $this->_sitemapHelper = $sitemapHelper; + $this->_filesystem = $filesystem; + $this->_sitemapFactory = $sitemapFactory; + } + /** * Validate path for generation * @@ -23,17 +78,25 @@ protected function validatePath(array $data) if (!empty($data['sitemap_filename']) && !empty($data['sitemap_path'])) { $data['sitemap_path'] = '/' . ltrim($data['sitemap_path'], '/'); $path = rtrim($data['sitemap_path'], '\\/') . '/' . $data['sitemap_filename']; - /** @var $validator \Magento\MediaStorage\Model\File\Validator\AvailablePath */ - $validator = $this->_objectManager->create(\Magento\MediaStorage\Model\File\Validator\AvailablePath::class); - /** @var $helper \Magento\Sitemap\Helper\Data */ - $helper = $this->_objectManager->get(\Magento\Sitemap\Helper\Data::class); - $validator->setPaths($helper->getValidPaths()); - if (!$validator->isValid($path)) { - foreach ($validator->getMessages() as $message) { + $this->_pathValidator->setPaths($this->_sitemapHelper->getValidPaths()); + if (!$this->_pathValidator->isValid($path)) { + foreach ($this->_pathValidator->getMessages() as $message) { + $this->messageManager->addErrorMessage($message); + } + // save data in session + $this->_session->setFormData($data); + // redirect to edit form + return false; + } + + $filename = rtrim($data['sitemap_filename']); + $this->_stringValidator->setMax(self::MAX_FILENAME_LENGTH); + if (!$this->_stringValidator->isValid($filename)) { + foreach ($this->_stringValidator->getMessages() as $message) { $this->messageManager->addErrorMessage($message); } // save data in session - $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setFormData($data); + $this->_session->setFormData($data); // redirect to edit form return false; } @@ -49,9 +112,8 @@ protected function validatePath(array $data) */ protected function clearSiteMap(\Magento\Sitemap\Model\Sitemap $model) { - /** @var \Magento\Framework\Filesystem\Directory\Write $directory */ - $directory = $this->_objectManager->get(\Magento\Framework\Filesystem::class) - ->getDirectoryWrite(DirectoryList::ROOT); + /** @var \Magento\Framework\Filesystem $directory */ + $directory = $this->_filesystem->getDirectoryWrite(DirectoryList::ROOT); if ($this->getRequest()->getParam('sitemap_id')) { $model->load($this->getRequest()->getParam('sitemap_id')); @@ -74,7 +136,7 @@ protected function saveData($data) { // init model and set data /** @var \Magento\Sitemap\Model\Sitemap $model */ - $model = $this->_objectManager->create(\Magento\Sitemap\Model\Sitemap::class); + $model = $this->_sitemapFactory->create(); $this->clearSiteMap($model); $model->setData($data); @@ -85,13 +147,13 @@ protected function saveData($data) // display success message $this->messageManager->addSuccessMessage(__('You saved the sitemap.')); // clear previously saved data from session - $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setFormData(false); + $this->_session->setFormData(false); return $model->getId(); } catch (\Exception $e) { // display error message $this->messageManager->addErrorMessage($e->getMessage()); // save data in session - $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setFormData($data); + $this->_session->setFormData($data); } return false; } diff --git a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php index f77954101df7c..0e5758a3d8f48 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php @@ -7,6 +7,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Framework\Controller\ResultFactory; +use Magento\Sitemap\Controller\Adminhtml\Sitemap\Save; class SaveTest extends \PHPUnit\Framework\TestCase { @@ -18,12 +19,7 @@ class SaveTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Backend\App\Action\Context */ - protected $context; - - /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager; - */ - protected $objectManagerHelper; + protected $contextMock; /** * @var \Magento\Framework\HTTP\PhpEnvironment\Request|\PHPUnit_Framework_MockObject_MockObject @@ -50,8 +46,41 @@ class SaveTest extends \PHPUnit\Framework\TestCase */ protected $messageManagerMock; + /** + * @var \Magento\Framework\Validator\StringLength|\PHPUnit_Framework_MockObject_MockObject + */ + protected $lengthValidator; + + /** + * @var \Magento\MediaStorage\Model\File\Validator\AvailablePath|\PHPUnit_Framework_MockObject_MockObject + */ + protected $pathValidator; + + /** + * @var \Magento\Sitemap\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + */ + protected $helper; + + /** + * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject + */ + protected $fileSystem; + + /** + * @var \Magento\Sitemap\Model\SitemapFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $siteMapFactory; + + /** + * @var \Magento\Backend\Model\Session|\PHPUnit_Framework_MockObject_MockObject + */ + protected $session; + protected function setUp() { + $this->contextMock = $this->getMockBuilder(\Magento\Backend\App\Action\Context::class) + ->disableOriginalConstructor() + ->getMock(); $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) ->disableOriginalConstructor() ->setMethods(['getPostValue']) @@ -66,27 +95,48 @@ protected function setUp() ->getMock(); $this->messageManagerMock = $this->getMockBuilder(\Magento\Framework\Message\ManagerInterface::class) ->getMock(); - + $this->helper = $this->getMockBuilder(\Magento\Sitemap\Helper\Data::class) + ->disableOriginalConstructor() + ->getMock(); $this->resultFactoryMock->expects($this->once()) ->method('create') ->with(ResultFactory::TYPE_REDIRECT) ->willReturn($this->resultRedirectMock); + $this->session = $this->getMockBuilder(\Magento\Backend\Model\Session::class) + ->disableOriginalConstructor() + ->setMethods(['setFormData']) + ->getMock(); - $this->objectManagerHelper = new ObjectManagerHelper($this); - $this->context = $this->objectManagerHelper->getObject( - \Magento\Backend\App\Action\Context::class, - [ - 'resultFactory' => $this->resultFactoryMock, - 'request' => $this->requestMock, - 'messageManager' => $this->messageManagerMock, - 'objectManager' => $this->objectManagerMock - ] - ); - $this->saveController = $this->objectManagerHelper->getObject( - \Magento\Sitemap\Controller\Adminhtml\Sitemap\Save::class, - [ - 'context' => $this->context - ] + $this->contextMock->expects($this->once()) + ->method('getMessageManager') + ->willReturn($this->messageManagerMock); + $this->contextMock->expects($this->once()) + ->method('getRequest') + ->willReturn($this->requestMock); + $this->contextMock->expects($this->once()) + ->method('getResultFactory') + ->willReturn($this->resultFactoryMock); + $this->contextMock->expects($this->once()) + ->method('getSession') + ->willReturn($this->session); + + $this->lengthValidator = $this->getMockBuilder(\Magento\Framework\Validator\StringLength::class) + ->disableOriginalConstructor() + ->getMock(); + $this->pathValidator = + $this->getMockBuilder(\Magento\MediaStorage\Model\File\Validator\AvailablePath::class) + ->disableOriginalConstructor() + ->getMock(); + $this->fileSystem = $this->createMock(\Magento\Framework\Filesystem::class); + $this->siteMapFactory = $this->createMock(\Magento\Sitemap\Model\SitemapFactory::class); + + $this->saveController = new Save( + $this->contextMock, + $this->lengthValidator, + $this->pathValidator, + $this->helper, + $this->fileSystem, + $this->siteMapFactory ); } @@ -105,11 +155,8 @@ public function testSaveEmptyDataShouldRedirectToDefault() public function testTryToSaveInvalidDataShouldFailWithErrors() { - $validatorClass = \Magento\MediaStorage\Model\File\Validator\AvailablePath::class; - $helperClass = \Magento\Sitemap\Helper\Data::class; $validPaths = []; $messages = ['message1', 'message2']; - $sessionClass = \Magento\Backend\Model\Session::class; $data = ['sitemap_filename' => 'sitemap_filename', 'sitemap_path' => '/sitemap_path']; $siteMapId = 1; @@ -121,37 +168,83 @@ public function testTryToSaveInvalidDataShouldFailWithErrors() ->with('sitemap_id') ->willReturn($siteMapId); - $validator = $this->createMock($validatorClass); - $validator->expects($this->once()) + $this->pathValidator->expects($this->once()) ->method('setPaths') ->with($validPaths) ->willReturnSelf(); - $validator->expects($this->once()) + $this->pathValidator->expects($this->once()) ->method('isValid') ->with('/sitemap_path/sitemap_filename') ->willReturn(false); - $validator->expects($this->once()) + $this->pathValidator->expects($this->once()) ->method('getMessages') ->willReturn($messages); - $helper = $this->createMock($helperClass); - $helper->expects($this->once()) + $this->helper->expects($this->once()) ->method('getValidPaths') ->willReturn($validPaths); - $session = $this->createPartialMock($sessionClass, ['setFormData']); - $session->expects($this->once()) + $this->session->expects($this->once()) ->method('setFormData') ->with($data) ->willReturnSelf(); - $this->objectManagerMock->expects($this->once()) - ->method('create') - ->with($validatorClass) - ->willReturn($validator); - $this->objectManagerMock->expects($this->any()) - ->method('get') - ->willReturnMap([[$helperClass, $helper], [$sessionClass, $session]]); + $this->messageManagerMock->expects($this->at(0)) + ->method('addErrorMessage') + ->withConsecutive( + [$messages[0]], + [$messages[1]] + ) + ->willReturnSelf(); + + $this->resultRedirectMock->expects($this->once()) + ->method('setPath') + ->with('adminhtml/*/edit', ['sitemap_id' => $siteMapId]) + ->willReturnSelf(); + + $this->assertSame($this->resultRedirectMock, $this->saveController->execute()); + } + + public function testTryToSaveInvalidFileNameShouldFailWithErrors() + { + $validPaths = []; + $messages = ['message1', 'message2']; + $data = ['sitemap_filename' => 'sitemap_filename', 'sitemap_path' => '/sitemap_path']; + $siteMapId = 1; + + $this->requestMock->expects($this->once()) + ->method('getPostValue') + ->willReturn($data); + $this->requestMock->expects($this->once()) + ->method('getParam') + ->with('sitemap_id') + ->willReturn($siteMapId); + + $this->lengthValidator->expects($this->once()) + ->method('isValid') + ->with('sitemap_filename') + ->willReturn(false); + $this->lengthValidator->expects($this->once()) + ->method('getMessages') + ->willReturn($messages); + + $this->pathValidator->expects($this->once()) + ->method('setPaths') + ->with($validPaths) + ->willReturnSelf(); + $this->pathValidator->expects($this->once()) + ->method('isValid') + ->with('/sitemap_path/sitemap_filename') + ->willReturn(true); + + $this->helper->expects($this->once()) + ->method('getValidPaths') + ->willReturn($validPaths); + + $this->session->expects($this->once()) + ->method('setFormData') + ->with($data) + ->willReturnSelf(); $this->messageManagerMock->expects($this->at(0)) ->method('addErrorMessage') From 825fa30f043cdee014dcb6602eed50616a2f601c Mon Sep 17 00:00:00 2001 From: shikhamis11 Date: Thu, 1 Nov 2018 17:59:28 +0530 Subject: [PATCH 013/254] fixed store wise product filter issue Fixed issue - #18374 Unable to get product attribute value for store-view scope type in product collection loaded for a specific store. --- .../Magento/Eav/Model/Entity/Collection/AbstractCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php index 0eb87374f3ba3..eba9976260bed 100644 --- a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php +++ b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php @@ -399,7 +399,7 @@ public function addAttributeToFilter($attribute, $condition = null, $joinType = */ public function addFieldToFilter($attribute, $condition = null) { - return $this->addAttributeToFilter($attribute, $condition); + return $this->addAttributeToFilter($attribute, $condition,'left'); } /** From fd52e94b69f2ec44552fb1ff21a3897d114f620a Mon Sep 17 00:00:00 2001 From: Stanislav Idolov Date: Fri, 21 Dec 2018 16:15:37 -0600 Subject: [PATCH 014/254] Fixed code style issue --- .../Magento/Eav/Model/Entity/Collection/AbstractCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php index eba9976260bed..6d9e5e88cdfe5 100644 --- a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php +++ b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php @@ -399,7 +399,7 @@ public function addAttributeToFilter($attribute, $condition = null, $joinType = */ public function addFieldToFilter($attribute, $condition = null) { - return $this->addAttributeToFilter($attribute, $condition,'left'); + return $this->addAttributeToFilter($attribute, $condition, 'left'); } /** From 12c04d69385ee41470de73400049bf84a7534aab Mon Sep 17 00:00:00 2001 From: DmytroPaidych Date: Wed, 9 Jan 2019 16:03:26 +0200 Subject: [PATCH 015/254] MAGETWO-97212: Enable skipped test - Move Anchored Category with Products (cron is ON, "Update on Save") --- .../Catalog/Test/Repository/Category.xml | 26 +++++++++++++++++++ .../Category/MoveCategoryEntityTest.xml | 6 ++--- .../Test/Block/Navigation.php | 24 ++++++++++++++--- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml index d529f74865985..014d685cfdb7c 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml @@ -179,5 +179,31 @@ catalogProductSimple::default + + DefaultSubcategory%isolation% + default-subcategory-%isolation% + + default_anchored_category_with_product + + Yes + Yes + Yes + + catalogProductSimple::default + + + + Category%isolation% + category%isolation% + Yes + Yes + Yes + + default_category + + + catalogProductSimple::product_5_dollar + + diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.xml index 446011902c096..b79769b5be4fe 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/MoveCategoryEntityTest.xml @@ -15,8 +15,7 @@ - MAGETWO-65147: Category is not present in Layered navigation block when anchor is on - default_subcategory_with_anchored_parent + default_subcategory_with_anchored_parent_with_product default 2 @@ -25,9 +24,8 @@ - MAGETWO-65147: Category is not present in Layered navigation block when anchor is on default_subcategory_with_anchored_parent - default_anchor_subcategory_with_anchored_parent + default_subcategory_with_anchored_parent_with_product default_category 2 diff --git a/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/Block/Navigation.php b/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/Block/Navigation.php index 3c36fe82b1307..9d2b6a0ed0e7a 100644 --- a/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/Block/Navigation.php +++ b/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/Block/Navigation.php @@ -64,6 +64,13 @@ class Navigation extends Block */ private $productQty = '/following-sibling::span[contains(text(), "%s")]'; + /** + * Selector for child element with product quantity. + * + * @var string + */ + private $productQtyInCategory = '/span[contains(text(), "%s")]'; + /** * Remove all applied filters. * @@ -124,10 +131,19 @@ public function applyFilter($filter, $linkPattern) */ public function isCategoryVisible(Category $category, $qty) { - return $this->_rootElement->find( - sprintf($this->categoryName, $category->getName()) . sprintf($this->productQty, $qty), - Locator::SELECTOR_XPATH - )->isVisible(); + $link = $this->_rootElement->find(sprintf($this->optionTitle, 'Category'), Locator::SELECTOR_XPATH); + if ($link->isVisible()) { + $link->click(); + return $this->_rootElement->find( + sprintf($this->categoryName, $category->getName()) . sprintf($this->productQtyInCategory, $qty), + Locator::SELECTOR_XPATH + )->isVisible(); + } else { + return $this->_rootElement->find( + sprintf($this->categoryName, $category->getName()) . sprintf($this->productQty, $qty), + Locator::SELECTOR_XPATH + )->isVisible(); + } } /** From fb821b4f18eaa7ad350529f8fbd2887af3bb7438 Mon Sep 17 00:00:00 2001 From: Oleg Onufer Date: Mon, 14 Jan 2019 16:52:42 +0200 Subject: [PATCH 016/254] MAGETWO-97240: Category rules should apply to complex products --- .../ActionGroup/AdminCategoryActionGroup.xml | 11 ++ ...reateApiConfigurableProductActionGroup.xml | 13 ++ .../AdminCartPriceRulesFormSection.xml | 3 + ...yRulesShouldApplyToComplexProductsTest.xml | 135 ++++++++++++++++++ 4 files changed, 162 insertions(+) create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 57f91b78fcbe9..138ae0370e965 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -263,4 +263,15 @@ + + + + + + + + + + + diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml index 033e6757c3bf9..c0a9f03906030 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminCreateApiConfigurableProductActionGroup.xml @@ -62,4 +62,17 @@ + + + + + + + + + + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index c3680e1c3ceaf..f528f97f0b386 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -41,9 +41,12 @@ + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml new file mode 100644 index 0000000000000..3762b69b745c4 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml @@ -0,0 +1,135 @@ + + + + + + + + + + <description value="Sales rules filtering on category should apply to all products, including complex products."/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-70192"/> + <group value="CatalogRule"/> + </annotations> + <before> + <!-- Create two Categories: CAT1 and CAT2 --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleSubCategory" stepKey="createCategory2"/> + <!--Create config1 and config2--> + <actionGroup ref="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" stepKey="createConfigurableProduct1"> + <argument name="productName" value="config1"/> + </actionGroup> + <actionGroup ref="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" stepKey="createConfigurableProduct2"> + <argument name="productName" value="config2"/> + </actionGroup> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Assign config1 and the associated child products to CAT1 --> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignProductToCategory"> + <argument name="productId" value="$$createConfigProductCreateConfigurableProduct1.id$$"/> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignProductToCategory2"> + <argument name="productId" value="$$createConfigChildProduct1CreateConfigurableProduct1.id$$"/> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignProductToCategory3"> + <argument name="productId" value="$$createConfigChildProduct2CreateConfigurableProduct1.id$$"/> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <!-- Assign config12 and the associated child products to CAT2 --> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignProductToCategory5"> + <argument name="productId" value="$$createConfigProductCreateConfigurableProduct2.id$$"/> + <argument name="categoryName" value="$$createCategory.name$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignProductToCategor5"> + <argument name="productId" value="$$createConfigChildProduct1CreateConfigurableProduct2.id$$"/> + <argument name="categoryName" value="$$createCategory2.name$$"/> + </actionGroup> + <actionGroup ref="AdminAssignProductToCategory" stepKey="assignProductToCategory6"> + <argument name="productId" value="$$createConfigChildProduct2CreateConfigurableProduct2.id$$"/> + <argument name="categoryName" value="$$createCategory2.name$$"/> + </actionGroup> + </before> + <after> + <!--Delete configurable product 1--> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct1" stepKey="deleteConfigProduct1"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory1"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct1" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct1" stepKey="deleteConfigProductAttribute1"/> + <!--Delete configurable product 2--> + <deleteData createDataKey="createConfigProductCreateConfigurableProduct2" stepKey="deleteConfigProduct2"/> + <deleteData createDataKey="createCategory2" stepKey="deleteCategory2"/> + <deleteData createDataKey="createConfigChildProduct1CreateConfigurableProduct2" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct2" stepKey="deleteConfigChildProduct4"/> + <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct2" stepKey="deleteConfigProductAttribute2"/> + <!--Delete Cart Price Rule --> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> + <argument name="ruleName" value="{{ApiSalesRule.name}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- 1: Create a cart price rule applying to CAT1 with discount --> + <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceGridPage"/> + <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> + <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{ApiSalesRule.name}}" stepKey="fillRuleName"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsite"/> + <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="chooseNotLoggedInCustomerGroup"/> + <!-- Open the Actions Tab--> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickOnActionTab"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="50" stepKey="fillDiscountAmount"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount" stepKey="selectActionType"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('ALL')}}" stepKey="clickToChooseOption2"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.actionsAggregator}}" userInput="ANY" stepKey="selectCondition"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('TRUE')}}" stepKey="clickToChooseOption3"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.actionsValue}}" userInput="FALSE" stepKey="selectCondition2"/> + <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="selectActionConditions"/> + <waitForPageLoad stepKey="waitForDropDownOpened"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="Category" stepKey="selectAttribute"/> + <waitForPageLoad stepKey="waitForOperatorOpened"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption1"/> + <fillField selector="{{AdminCartPriceRulesFormSection.actionValue}}" userInput="$$createCategory.id$$" stepKey="fillActionValue"/> + <click selector="{{AdminCartPriceRulesFormSection.applyAction}}" stepKey="applyAction"/> + <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + <!-- 2: Go to frontend and add an item from both CAT1 and CAT2 to your cart --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontend"/> + <!-- 3: Open configurable product 1 and add all his child products to cart --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProductCreateConfigurableProduct1.custom_attributes[url_key]$$)}}" stepKey="amOnConfigurableProductPage"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct1.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption1CreateConfigurableProduct1.option[store_labels][0][label]$$" stepKey="selectOption"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart"> + <argument name="product" value="$$createConfigProductCreateConfigurableProduct1$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct1.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption2CreateConfigurableProduct1.option[store_labels][0][label]$$" stepKey="selectOption2"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart2"> + <argument name="product" value="$$createConfigProductCreateConfigurableProduct1$$"/> + <argument name="productCount" value="2"/> + </actionGroup> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCart"/> + <!-- Discount amount is not applied --> + <dontSee selector="{{CheckoutCartSummarySection.discountLabel}}" stepKey="discountIsNotApply"/> + <!-- 3: Open configurable product 2 and add all his child products to cart --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProductCreateConfigurableProduct2.custom_attributes[url_key]$$)}}" stepKey="amOnConfigurableProductPage2"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct2.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption1CreateConfigurableProduct2.option[store_labels][0][label]$$" stepKey="selectOption3"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart3"> + <argument name="product" value="$$createConfigProductCreateConfigurableProduct2$$"/> + <argument name="productCount" value="3"/> + </actionGroup> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect('$$createConfigProductAttributeCreateConfigurableProduct2.attribute[frontend_labels][0][label]$$')}}" userInput="$$createConfigProductAttributeOption2CreateConfigurableProduct2.option[store_labels][0][label]$$" stepKey="selectOption4"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="cartAddConfigurableProductToCart4"> + <argument name="product" value="$$createConfigProductCreateConfigurableProduct2$$"/> + <argument name="productCount" value="4"/> + </actionGroup> + <!-- Discount amount is applied --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToCart2"/> + <see selector="{{CheckoutCartSummarySection.discountTotal}}" userInput="-$100.00" stepKey="discountIsApply"/> + </test> +</tests> From 99d6e43098dbc33dcfbd922521e685eaa791837f Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Tue, 15 Jan 2019 17:27:03 +0530 Subject: [PATCH 017/254] Updated _payment.less file to fix update button issue Updated _payment.less file to fix update button issue --- .../web/css/source/module/checkout/_payments.less | 1 + 1 file changed, 1 insertion(+) 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 dd9db0e715308..d7e3de57f4657 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 @@ -118,6 +118,7 @@ .primary { .action-update { margin-right: 0; + margin-bottom:20px; } } From da46b6ac353d1cbd9e58686516d31a9025110db1 Mon Sep 17 00:00:00 2001 From: Govind Sharma <govindpokhrelsharma@cedcoss.com> Date: Wed, 16 Jan 2019 08:50:36 +0530 Subject: [PATCH 018/254] fix indent --- .../web/css/source/module/checkout/_payments.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d7e3de57f4657..7428b1a108b25 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 @@ -118,7 +118,7 @@ .primary { .action-update { margin-right: 0; - margin-bottom:20px; + margin-bottom: 20px; } } From 74b2098d878e71ec39284019ad887edce815f049 Mon Sep 17 00:00:00 2001 From: Arvinda kumar <arvindakumar@cedcoss.com> Date: Wed, 16 Jan 2019 18:07:31 +0530 Subject: [PATCH 019/254] issue fixed #20337 Option Title breaking in two line because applying wrong css for manage width issue fixed #20337 Option Title breaking in two line because applying wrong css for manage width --- .../adminhtml/Magento/backend/web/css/source/forms/_fields.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 02925881253ea..81a55443d6a3c 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 @@ -531,7 +531,7 @@ & > .admin__field { &:first-child { position: static; - + width: 100%; & > .admin__field-label { #mix-grid .column(@field-label-grid__column, @field-grid__columns); cursor: pointer; From 25f3d7d5508dd84221a6d4799b0734b8ddafc68d Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Wed, 16 Jan 2019 15:25:51 +0200 Subject: [PATCH 020/254] MAGETWO-97240: Category rules should apply to complex products --- .../ActionGroup/AdminCategoryActionGroup.xml | 2 +- .../AdminCartPriceRuleActionGroup.xml | 23 ++++++++++++ .../Test/Mftf/Data/SalesRuleData.xml | 4 +++ ...yRulesShouldApplyToComplexProductsTest.xml | 35 +++++-------------- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 138ae0370e965..a5c26d2530a66 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -272,6 +272,6 @@ <amOnPage url="{{AdminProductEditPage.url(productId)}}" stepKey="amOnPage"/> <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{categoryName}}]" stepKey="selectCategory"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> - <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveProductMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml index e5907e1e9c0f5..2b8b5eb4ad17c 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml @@ -38,4 +38,27 @@ <click selector="{{AdminMainActionsSection.saveAndContinue}}" stepKey="clickSaveButton"/> <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> </actionGroup> + + <actionGroup name="SetConditionForActionsInCartPriceRuleActionGroup"> + <arguments> + <argument name="actionsAggregator" type="string" defaultValue="ANY"/> + <argument name="actionsValue" type="string" defaultValue="FALSE"/> + <argument name="childAttribute" type="string" defaultValue="Category"/> + <argument name="actionValue"/> + </arguments> + <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickOnActionTab"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('ALL')}}" stepKey="clickToChooseOption"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.actionsAggregator}}" userInput="{{actionsAggregator}}" stepKey="selectCondition"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('TRUE')}}" stepKey="clickToChooseOption2"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.actionsValue}}" userInput="{{actionsValue}}" stepKey="selectCondition2"/> + <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="selectActionConditions"/> + <waitForPageLoad stepKey="waitForDropDownOpened"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="{{childAttribute}}" stepKey="selectAttribute"/> + <waitForPageLoad stepKey="waitForOperatorOpened"/> + <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption3"/> + <fillField selector="{{AdminCartPriceRulesFormSection.actionValue}}" userInput="{{actionValue}}" stepKey="fillActionValue"/> + <click selector="{{AdminCartPriceRulesFormSection.applyAction}}" stepKey="applyAction"/> + <click selector="{{AdminMainActionsSection.save}}" stepKey="clickSaveButton"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index ded1d2806a684..0fbc68a773122 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -171,4 +171,8 @@ <data key="uses_per_coupon">10</data> <data key="simple_free_shipping">1</data> </entity> + + <entity name="SalesRuleNoCouponWithFixedDiscount" extends="ApiCartRule"> + <data key="simple_action">by_fixed</data> + </entity> </entities> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml index 3762b69b745c4..52598e98fd563 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml @@ -16,11 +16,11 @@ <description value="Sales rules filtering on category should apply to all products, including complex products."/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-70192"/> - <group value="CatalogRule"/> + <group value="catalogRule"/> </annotations> <before> <!-- Create two Categories: CAT1 and CAT2 --> - <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> <createData entity="SimpleSubCategory" stepKey="createCategory2"/> <!--Create config1 and config2--> <actionGroup ref="AdminCreateApiConfigurableProductWithHiddenChildActionGroup" stepKey="createConfigurableProduct1"> @@ -71,34 +71,15 @@ <deleteData createDataKey="createConfigChildProduct2CreateConfigurableProduct2" stepKey="deleteConfigChildProduct4"/> <deleteData createDataKey="createConfigProductAttributeCreateConfigurableProduct2" stepKey="deleteConfigProductAttribute2"/> <!--Delete Cart Price Rule --> - <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> - <argument name="ruleName" value="{{ApiSalesRule.name}}"/> - </actionGroup> + <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/> <actionGroup ref="logout" stepKey="logout"/> </after> <!-- 1: Create a cart price rule applying to CAT1 with discount --> - <amOnPage url="{{AdminCartPriceRulesPage.url}}" stepKey="amOnCartPriceGridPage"/> - <click selector="{{AdminCartPriceRulesSection.addNewRuleButton}}" stepKey="clickAddNewRule"/> - <fillField selector="{{AdminCartPriceRulesFormSection.ruleName}}" userInput="{{ApiSalesRule.name}}" stepKey="fillRuleName"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.websites}}" userInput="Main Website" stepKey="selectWebsite"/> - <actionGroup ref="selectNotLoggedInCustomerGroup" stepKey="chooseNotLoggedInCustomerGroup"/> - <!-- Open the Actions Tab--> - <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickOnActionTab"/> - <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="50" stepKey="fillDiscountAmount"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount" stepKey="selectActionType"/> - <click selector="{{AdminCartPriceRulesFormSection.condition('ALL')}}" stepKey="clickToChooseOption2"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.actionsAggregator}}" userInput="ANY" stepKey="selectCondition"/> - <click selector="{{AdminCartPriceRulesFormSection.condition('TRUE')}}" stepKey="clickToChooseOption3"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.actionsValue}}" userInput="FALSE" stepKey="selectCondition2"/> - <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="selectActionConditions"/> - <waitForPageLoad stepKey="waitForDropDownOpened"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="Category" stepKey="selectAttribute"/> - <waitForPageLoad stepKey="waitForOperatorOpened"/> - <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption1"/> - <fillField selector="{{AdminCartPriceRulesFormSection.actionValue}}" userInput="$$createCategory.id$$" stepKey="fillActionValue"/> - <click selector="{{AdminCartPriceRulesFormSection.applyAction}}" stepKey="applyAction"/> - <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> - <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> + <createData entity="SalesRuleNoCouponWithFixedDiscount" stepKey="createCartPriceRule"/> + <amOnPage url="{{AdminCartPriceRuleEditPage.url($$createCartPriceRule.rule_id$$)}}" stepKey="goToCartPriceRuleEditPage"/> + <actionGroup ref="SetConditionForActionsInCartPriceRuleActionGroup" stepKey="setConditionForActionsInCartPriceRuleActionGroup"> + <argument name="actionValue" value="$$createCategory.id$$"/> + </actionGroup> <!-- 2: Go to frontend and add an item from both CAT1 and CAT2 to your cart --> <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToFrontend"/> <!-- 3: Open configurable product 1 and add all his child products to cart --> From f50d5accea7f43cf5100772cc6fd90cdc15702ff Mon Sep 17 00:00:00 2001 From: Oleg Onufer <linkedddd@gmail.com> Date: Thu, 17 Jan 2019 14:52:32 +0200 Subject: [PATCH 021/254] MAGETWO-97240: Category rules should apply to complex products --- .../Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml index 2b8b5eb4ad17c..210259f474ee9 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCartPriceRuleActionGroup.xml @@ -44,7 +44,7 @@ <argument name="actionsAggregator" type="string" defaultValue="ANY"/> <argument name="actionsValue" type="string" defaultValue="FALSE"/> <argument name="childAttribute" type="string" defaultValue="Category"/> - <argument name="actionValue"/> + <argument name="actionValue" type="string"/> </arguments> <click selector="{{AdminCartPriceRulesFormSection.actionsHeader}}" stepKey="clickOnActionTab"/> <click selector="{{AdminCartPriceRulesFormSection.condition('ALL')}}" stepKey="clickToChooseOption"/> From 20fd58914382220cb4b5050d0540ec8f62c63814 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 18 Jan 2019 00:00:55 +0200 Subject: [PATCH 022/254] add mage/adminhtml/tools for Base64 --- app/code/Magento/Variable/view/adminhtml/web/variables.js | 3 ++- .../wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Variable/view/adminhtml/web/variables.js b/app/code/Magento/Variable/view/adminhtml/web/variables.js index 47f027f27102d..bf8bfbc570ce2 100644 --- a/app/code/Magento/Variable/view/adminhtml/web/variables.js +++ b/app/code/Magento/Variable/view/adminhtml/web/variables.js @@ -16,7 +16,8 @@ define([ 'Magento_Variable/js/custom-directive-generator', 'Magento_Ui/js/lib/spinner', 'jquery/ui', - 'prototype' + 'prototype', + 'mage/adminhtml/tools' ], function (jQuery, notification, $t, wysiwyg, registry, mageApply, utils, configGenerator, customGenerator, loader) { 'use strict'; diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js index e6f12a2e51acf..96091e4099676 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/plugins/magentovariable/editor_plugin.js @@ -9,7 +9,8 @@ define([ 'Magento_Variable/js/config-directive-generator', 'Magento_Variable/js/custom-directive-generator', 'wysiwygAdapter', - 'jquery' + 'jquery', + 'mage/adminhtml/tools' ], function (configDirectiveGenerator, customDirectiveGenerator, wysiwyg, jQuery) { return function (config) { tinymce.create('tinymce.plugins.magentovariable', { From ddb1137b96ff0ae93c2e78dff62e02da31f9a02a Mon Sep 17 00:00:00 2001 From: Vivek Kumar <vivekkumar@cedcommerce.com> Date: Thu, 17 Jan 2019 17:53:04 +0530 Subject: [PATCH 023/254] Issue Fixed: #8086: Multiline admin field is broken --- app/code/Magento/Config/Model/Config.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/Config/Model/Config.php b/app/code/Magento/Config/Model/Config.php index b1074e92cc949..bec44e9d55757 100644 --- a/app/code/Magento/Config/Model/Config.php +++ b/app/code/Magento/Config/Model/Config.php @@ -424,6 +424,11 @@ protected function _processGroup( if (!isset($fieldData['value'])) { $fieldData['value'] = null; } + + if ($field->getType() == 'multiline' && is_array($fieldData['value'])) { + $fieldData['value'] = trim(implode(PHP_EOL, $fieldData['value'])); + } + $data = [ 'field' => $fieldId, 'groups' => $groups, From 9ca8077242bd6bb7a09c8bb77f4495b39787fcb0 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 18 Jan 2019 14:07:02 +0200 Subject: [PATCH 024/254] Update app/code/Magento/Eav/Model/Attribute/Data/File.php Co-Authored-By: Nazar65 <nazarn96@gmail.com> --- app/code/Magento/Eav/Model/Attribute/Data/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Attribute/Data/File.php b/app/code/Magento/Eav/Model/Attribute/Data/File.php index b8ca34986111b..8a29c1f2326cf 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/File.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/File.php @@ -146,7 +146,7 @@ protected function _validateByRules($value) return $this->_fileValidator->getMessages(); } - if (empty($value['tmp_name']) && !is_uploaded_file($value['tmp_name'])) { + if (empty($value['tmp_name']) || !is_uploaded_file($value['tmp_name'])) { return [__('"%1" is not a valid file.', $label)]; } From 035e8d736961c1a074b8a732ec3bbd993670aacc Mon Sep 17 00:00:00 2001 From: rajneesh1dev <rajneeshgupta@cedcoss.com> Date: Tue, 22 Jan 2019 11:24:14 +0530 Subject: [PATCH 025/254] Updated phpdocs, property access levels,removed underscore, added backward compatible constructor params --- .../Controller/Adminhtml/Sitemap/Save.php | 56 +++++++++---------- .../Controller/Adminhtml/Sitemap/SaveTest.php | 26 ++++----- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php index 23e1d2e786900..d0cb650028e99 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php @@ -17,29 +17,29 @@ class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap const MAX_FILENAME_LENGTH = 32; /** - * @var $_stringValidator + * @var \Magento\Framework\Validator\StringLength */ - public $_stringValidator; + private $stringValidator; /** - * @var $_pathValidator + * @var \Magento\MediaStorage\Model\File\Validator\AvailablePath */ - public $_pathValidator; + private $pathValidator; /** - * @var $_sitemapHelper + * @var \Magento\Sitemap\Helper\Data */ - public $_sitemapHelper; + private $sitemapHelper; /** - * @var $_filesystem + * @var \Magento\Framework\Filesystem */ - public $_filesystem; + private $filesystem; /** - * @var $_sitemapFactory + * @var \Magento\Sitemap\Model\SitemapFactory */ - public $_sitemapFactory; + private $sitemapFactory; /** * Save constructor. @@ -52,18 +52,18 @@ class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap */ public function __construct( \Magento\Backend\App\Action\Context $context, - \Magento\Framework\Validator\StringLength $stringValidator, - \Magento\MediaStorage\Model\File\Validator\AvailablePath $pathValidator, - \Magento\Sitemap\Helper\Data $sitemapHelper, - \Magento\Framework\Filesystem $filesystem, - \Magento\Sitemap\Model\SitemapFactory $sitemapFactory + \Magento\Framework\Validator\StringLength $stringValidator = null, + \Magento\MediaStorage\Model\File\Validator\AvailablePath $pathValidator = null, + \Magento\Sitemap\Helper\Data $sitemapHelper = null, + \Magento\Framework\Filesystem $filesystem = null, + \Magento\Sitemap\Model\SitemapFactory $sitemapFactory = null ) { parent::__construct($context); - $this->_stringValidator = $stringValidator; - $this->_pathValidator = $pathValidator; - $this->_sitemapHelper = $sitemapHelper; - $this->_filesystem = $filesystem; - $this->_sitemapFactory = $sitemapFactory; + $this->stringValidator = $stringValidator ?: $this->_objectManager->get(\Magento\Framework\Validator\StringLength::class); + $this->pathValidator = $pathValidator ?: $this->_objectManager->get(\Magento\MediaStorage\Model\File\Validator\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(\Magento\Sitemap\Model\SitemapFactory::class); } /** @@ -78,9 +78,9 @@ protected function validatePath(array $data) if (!empty($data['sitemap_filename']) && !empty($data['sitemap_path'])) { $data['sitemap_path'] = '/' . ltrim($data['sitemap_path'], '/'); $path = rtrim($data['sitemap_path'], '\\/') . '/' . $data['sitemap_filename']; - $this->_pathValidator->setPaths($this->_sitemapHelper->getValidPaths()); - if (!$this->_pathValidator->isValid($path)) { - foreach ($this->_pathValidator->getMessages() as $message) { + $this->pathValidator->setPaths($this->sitemapHelper->getValidPaths()); + if (!$this->pathValidator->isValid($path)) { + foreach ($this->pathValidator->getMessages() as $message) { $this->messageManager->addErrorMessage($message); } // save data in session @@ -90,9 +90,9 @@ protected function validatePath(array $data) } $filename = rtrim($data['sitemap_filename']); - $this->_stringValidator->setMax(self::MAX_FILENAME_LENGTH); - if (!$this->_stringValidator->isValid($filename)) { - foreach ($this->_stringValidator->getMessages() as $message) { + $this->stringValidator->setMax(self::MAX_FILENAME_LENGTH); + if (!$this->stringValidator->isValid($filename)) { + foreach ($this->stringValidator->getMessages() as $message) { $this->messageManager->addErrorMessage($message); } // save data in session @@ -113,7 +113,7 @@ protected function validatePath(array $data) protected function clearSiteMap(\Magento\Sitemap\Model\Sitemap $model) { /** @var \Magento\Framework\Filesystem $directory */ - $directory = $this->_filesystem->getDirectoryWrite(DirectoryList::ROOT); + $directory = $this->filesystem->getDirectoryWrite(DirectoryList::ROOT); if ($this->getRequest()->getParam('sitemap_id')) { $model->load($this->getRequest()->getParam('sitemap_id')); @@ -136,7 +136,7 @@ protected function saveData($data) { // init model and set data /** @var \Magento\Sitemap\Model\Sitemap $model */ - $model = $this->_sitemapFactory->create(); + $model = $this->sitemapFactory->create(); $this->clearSiteMap($model); $model->setData($data); diff --git a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php index 0e5758a3d8f48..70a8ab700d301 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php @@ -14,67 +14,67 @@ class SaveTest extends \PHPUnit\Framework\TestCase /** * @var \Magento\Sitemap\Controller\Adminhtml\Sitemap\Save */ - protected $saveController; + private $saveController; /** * @var \Magento\Backend\App\Action\Context */ - protected $contextMock; + private $contextMock; /** * @var \Magento\Framework\HTTP\PhpEnvironment\Request|\PHPUnit_Framework_MockObject_MockObject */ - protected $requestMock; + private $requestMock; /** * @var \Magento\Framework\Controller\ResultFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $resultFactoryMock; + private $resultFactoryMock; /** * @var \Magento\Backend\Model\View\Result\Redirect|\PHPUnit_Framework_MockObject_MockObject */ - protected $resultRedirectMock; + private $resultRedirectMock; /** * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $objectManagerMock; + private $objectManagerMock; /** * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $messageManagerMock; + private $messageManagerMock; /** * @var \Magento\Framework\Validator\StringLength|\PHPUnit_Framework_MockObject_MockObject */ - protected $lengthValidator; + private $lengthValidator; /** * @var \Magento\MediaStorage\Model\File\Validator\AvailablePath|\PHPUnit_Framework_MockObject_MockObject */ - protected $pathValidator; + private $pathValidator; /** * @var \Magento\Sitemap\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ - protected $helper; + private $helper; /** * @var \Magento\Framework\Filesystem|\PHPUnit_Framework_MockObject_MockObject */ - protected $fileSystem; + private $fileSystem; /** * @var \Magento\Sitemap\Model\SitemapFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $siteMapFactory; + private $siteMapFactory; /** * @var \Magento\Backend\Model\Session|\PHPUnit_Framework_MockObject_MockObject */ - protected $session; + private $session; protected function setUp() { From f669a4d2982579d768cb9c09fecceb720e3582e4 Mon Sep 17 00:00:00 2001 From: Nainesh <nainesh@2jcommerce.in> Date: Thu, 17 Jan 2019 16:54:48 +0530 Subject: [PATCH 026/254] 'Fixes-for-customer-login-page-input-field' :: On customer login page input field are short width on tablet view --- .../Magento/blank/Magento_Customer/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 9df59ca5dac92..a94fedbcbbd14 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 @@ -421,7 +421,7 @@ > .field { > .control { - width: 55%; + width: 80%; } } } 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 2e7856d390bd0..61fc2610fc2e5 100755 --- 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 @@ -375,7 +375,7 @@ .fieldset { > .field { > .control { - width: 55%; + width: 80%; } } } From 40a62741285b064fd9e4850244605e2dfed44a16 Mon Sep 17 00:00:00 2001 From: Nainesh <nainesh@2jcommerce.in> Date: Thu, 17 Jan 2019 16:54:48 +0530 Subject: [PATCH 027/254] 'Fixes-for-customer-login-page-input-field' :: On customer login page input field are short width on tablet view --- .../Magento/blank/Magento_Customer/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 9df59ca5dac92..a94fedbcbbd14 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 @@ -421,7 +421,7 @@ > .field { > .control { - width: 55%; + width: 80%; } } } 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 2e7856d390bd0..61fc2610fc2e5 100755 --- 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 @@ -375,7 +375,7 @@ .fieldset { > .field { > .control { - width: 55%; + width: 80%; } } } From 207a1594c13921d943cf1f96a4c781ea94ac7f05 Mon Sep 17 00:00:00 2001 From: Wouter Samaey <wouter.samaey@storefront.be> Date: Thu, 24 Jan 2019 13:57:43 +0100 Subject: [PATCH 028/254] Added original exception as the cause to the new exception on product delete error --- app/code/Magento/Catalog/Model/ProductRepository.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index d124bf5e42639..91830482b9250 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -647,7 +647,8 @@ public function delete(ProductInterface $product) throw new CouldNotSaveException(__($e->getMessage())); } catch (\Exception $e) { throw new \Magento\Framework\Exception\StateException( - __('The "%1" product couldn\'t be removed.', $sku) + __('The "%1" product couldn\'t be removed.', $sku), + $e ); } $this->removeProductFromLocalCache($sku); From 02d7b9329bb78511761d905951b7abb9428e29ae Mon Sep 17 00:00:00 2001 From: Wouter Samaey <wouter.samaey@storefront.be> Date: Thu, 24 Jan 2019 14:20:46 +0100 Subject: [PATCH 029/254] =?UTF-8?q?Added=20another=20=E2=80=9Ccause?= =?UTF-8?q?=E2=80=9D=20exception=20to=20the=20new=20exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/code/Magento/Catalog/Model/ProductRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php index 91830482b9250..48f45d0ce9373 100644 --- a/app/code/Magento/Catalog/Model/ProductRepository.php +++ b/app/code/Magento/Catalog/Model/ProductRepository.php @@ -644,7 +644,7 @@ public function delete(ProductInterface $product) unset($this->instancesById[$product->getId()]); $this->resourceModel->delete($product); } catch (ValidatorException $e) { - throw new CouldNotSaveException(__($e->getMessage())); + throw new CouldNotSaveException(__($e->getMessage()), $e); } catch (\Exception $e) { throw new \Magento\Framework\Exception\StateException( __('The "%1" product couldn\'t be removed.', $sku), From 122e5c2f61bd29fa6a329af2f23f4441a64b1ea9 Mon Sep 17 00:00:00 2001 From: LisovyiEvhenii <lisovyievhenii@gmail.com> Date: Thu, 24 Jan 2019 15:34:04 +0200 Subject: [PATCH 030/254] #13982: Customer Login Block sets the title for the page when rendered. Move setPageTitle from Block to layout configs --- app/code/Magento/Customer/Block/Form/Login.php | 9 --------- .../view/frontend/layout/customer_account_login.xml | 5 +++++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Customer/Block/Form/Login.php b/app/code/Magento/Customer/Block/Form/Login.php index 7b265ae1f0f32..d3d3306a49b44 100644 --- a/app/code/Magento/Customer/Block/Form/Login.php +++ b/app/code/Magento/Customer/Block/Form/Login.php @@ -47,15 +47,6 @@ public function __construct( $this->_customerSession = $customerSession; } - /** - * @return $this - */ - protected function _prepareLayout() - { - $this->pageConfig->getTitle()->set(__('Customer Login')); - return parent::_prepareLayout(); - } - /** * Retrieve form posting url * diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml b/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml index d49dae6dee58f..00f9c4ed84d24 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml @@ -7,6 +7,11 @@ --> <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="page.main.title"> + <action method="setPageTitle"> + <argument name="title" translate="true" xsi:type="string">Customer Login</argument> + </action> + </referenceBlock> <referenceContainer name="content"> <!-- customer.form.login.extra --> <container name="customer.login.container" label="Customer Login Container" htmlTag="div" htmlClass="login-container"> From 40d9c7e452eaae742f35ea587bbc6f8a2d3fc8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bajsarowicz?= <lukasz.bajsarowicz@gmail.com> Date: Thu, 24 Jan 2019 20:21:00 +0100 Subject: [PATCH 031/254] Optimize snail_case replacement to PascalCase --- .../Magento/Catalog/Controller/Adminhtml/Product/GridOnly.php | 2 +- .../Unit/Model/Category/CurrentUrlRewritesRegeneratorTest.php | 2 +- .../Unit/Model/Product/CurrentUrlRewritesRegeneratorTest.php | 2 +- .../Test/Unit/Observer/AfterImportDataObserverTest.php | 2 +- .../Tax/Test/Unit/Model/Calculation/RateRepositoryTest.php | 2 +- .../Tax/Test/Unit/Model/Sales/Total/Quote/ShippingTest.php | 2 +- .../Test/Fixture/CatalogSearchQuery/QueryText.php | 2 +- .../tests/app/Magento/Widget/Test/Handler/Widget/Curl.php | 2 +- .../Magento/Framework/Api/SimpleDataObjectConverter.php | 4 ++-- lib/internal/Magento/Framework/Code/NameBuilder.php | 2 +- lib/internal/Magento/Framework/DataObject.php | 4 ++-- lib/internal/Magento/Framework/DataObject/Copy.php | 4 ++-- lib/internal/Magento/Framework/Module/PackageInfo.php | 3 +-- 13 files changed, 16 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/GridOnly.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/GridOnly.php index 40e62895caffc..2ddd16b00dfd0 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/GridOnly.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/GridOnly.php @@ -47,7 +47,7 @@ public function execute() $this->productBuilder->build($this->getRequest()); $block = $this->getRequest()->getParam('gridOnlyBlock'); - $blockClassSuffix = str_replace(' ', '_', ucwords(str_replace('_', ' ', $block))); + $blockClassSuffix = ucwords($block, '_'); /** @var \Magento\Framework\Controller\Result\Raw $resultRaw */ $resultRaw = $this->resultRawFactory->create(); diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/CurrentUrlRewritesRegeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/CurrentUrlRewritesRegeneratorTest.php index fbc620a6d741a..294cf8562906d 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/CurrentUrlRewritesRegeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Category/CurrentUrlRewritesRegeneratorTest.php @@ -252,7 +252,7 @@ protected function getCurrentRewritesMocks($currentRewrites) ->disableOriginalConstructor()->getMock(); foreach ($urlRewrite as $key => $value) { $url->expects($this->any()) - ->method('get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key)))) + ->method('get' . str_replace('_', '', ucwords($key, '_'))) ->will($this->returnValue($value)); } $rewrites[] = $url; diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/CurrentUrlRewritesRegeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/CurrentUrlRewritesRegeneratorTest.php index 4855478b8488a..c431743fc0b51 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/CurrentUrlRewritesRegeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/Product/CurrentUrlRewritesRegeneratorTest.php @@ -294,7 +294,7 @@ protected function getCurrentRewritesMocks($currentRewrites) ->disableOriginalConstructor()->getMock(); foreach ($urlRewrite as $key => $value) { $url->expects($this->any()) - ->method('get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key)))) + ->method('get' . str_replace('_', '', ucwords($key, '_'))) ->will($this->returnValue($value)); } $rewrites[] = $url; diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/AfterImportDataObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/AfterImportDataObserverTest.php index fd9ab10537f1c..3984d949332d3 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/AfterImportDataObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/AfterImportDataObserverTest.php @@ -694,7 +694,7 @@ protected function currentUrlRewritesRegeneratorGetCurrentRewritesMocks($current ->disableOriginalConstructor()->getMock(); foreach ($urlRewrite as $key => $value) { $url->expects($this->any()) - ->method('get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key)))) + ->method('get' . str_replace('_', '', ucwords($key, '_'))) ->will($this->returnValue($value)); } $rewrites[] = $url; diff --git a/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateRepositoryTest.php b/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateRepositoryTest.php index bf49f3d479132..77da6950fecf7 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateRepositoryTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Calculation/RateRepositoryTest.php @@ -252,7 +252,7 @@ private function getTaxRateMock(array $taxRateData) foreach ($taxRateData as $key => $value) { // convert key from snake case to upper case $taxRateMock->expects($this->any()) - ->method('get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key)))) + ->method('get' . str_replace('_', '', ucwords($key, '_'))) ->will($this->returnValue($value)); } diff --git a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/ShippingTest.php b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/ShippingTest.php index 77e25d6f14574..2bfebc984bb81 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/ShippingTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/ShippingTest.php @@ -133,7 +133,7 @@ private function getMockObject($className, array $objectState) $getterValueMap = []; $methods = ['__wakeup']; foreach ($objectState as $key => $value) { - $getterName = 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))); + $getterName = 'get' . str_replace('_', '', ucwords($key, '_')); $getterValueMap[$getterName] = $value; $methods[] = $getterName; } diff --git a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery/QueryText.php b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery/QueryText.php index e2193b799c3be..11a8693723f25 100644 --- a/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery/QueryText.php +++ b/dev/tests/functional/tests/app/Magento/CatalogSearch/Test/Fixture/CatalogSearchQuery/QueryText.php @@ -61,7 +61,7 @@ private function createProducts(FixtureFactory $fixtureFactory, $productsData) $searchValue = isset($productData[2]) ? $productData[2] : $productData[1]; if ($this->data === null) { if ($product->hasData($searchValue)) { - $getProperty = 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $searchValue))); + $getProperty = 'get' . str_replace('_', '', ucwords($searchValue, '_')); $this->data = $product->$getProperty(); } else { $this->data = $searchValue; diff --git a/dev/tests/functional/tests/app/Magento/Widget/Test/Handler/Widget/Curl.php b/dev/tests/functional/tests/app/Magento/Widget/Test/Handler/Widget/Curl.php index 1a024eefe162d..13c16c888fbb0 100644 --- a/dev/tests/functional/tests/app/Magento/Widget/Test/Handler/Widget/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Widget/Test/Handler/Widget/Curl.php @@ -172,7 +172,7 @@ protected function prepareWidgetInstance(array $data) $widgetInstances = []; foreach ($data['widget_instance'] as $key => $widgetInstance) { $pageGroup = $widgetInstance['page_group']; - $method = 'prepare' . str_replace(' ', '', ucwords(str_replace('_', ' ', $pageGroup))) . 'Group'; + $method = 'prepare' . str_replace('_', '', ucwords($pageGroup, '_')) . 'Group'; if (!method_exists(__CLASS__, $method)) { throw new \Exception('Method for prepare page group "' . $method . '" is not exist.'); } diff --git a/lib/internal/Magento/Framework/Api/SimpleDataObjectConverter.php b/lib/internal/Magento/Framework/Api/SimpleDataObjectConverter.php index 49d824a4f2e5a..4dbf4680f8988 100644 --- a/lib/internal/Magento/Framework/Api/SimpleDataObjectConverter.php +++ b/lib/internal/Magento/Framework/Api/SimpleDataObjectConverter.php @@ -58,7 +58,7 @@ public function convertKeysToCamelCase(array $dataArray) if (is_array($fieldValue) && !$this->_isSimpleSequentialArray($fieldValue)) { $fieldValue = $this->convertKeysToCamelCase($fieldValue); } - $fieldName = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $fieldName)))); + $fieldName = lcfirst(str_replace('_', '', ucwords($fieldName, '_'))); $response[$fieldName] = $fieldValue; } return $response; @@ -148,7 +148,7 @@ protected function _unpackAssociativeArray($data) */ public static function snakeCaseToUpperCamelCase($input) { - return str_replace(' ', '', ucwords(str_replace('_', ' ', $input))); + return str_replace('_', '', ucwords($input, '_')); } /** diff --git a/lib/internal/Magento/Framework/Code/NameBuilder.php b/lib/internal/Magento/Framework/Code/NameBuilder.php index c27a896b65f04..13a17d7c13b0f 100644 --- a/lib/internal/Magento/Framework/Code/NameBuilder.php +++ b/lib/internal/Magento/Framework/Code/NameBuilder.php @@ -23,7 +23,7 @@ public function buildClassName($parts) $separator = '\\'; $string = join($separator, $parts); $string = str_replace('_', $separator, $string); - $className = str_replace(' ', $separator, ucwords(str_replace($separator, ' ', $string))); + $className = ucwords($string, $separator); return $className; } } diff --git a/lib/internal/Magento/Framework/DataObject.php b/lib/internal/Magento/Framework/DataObject.php index 9ff004c53bb9b..7f056d38c8fd7 100644 --- a/lib/internal/Magento/Framework/DataObject.php +++ b/lib/internal/Magento/Framework/DataObject.php @@ -202,7 +202,7 @@ protected function _getData($key) */ public function setDataUsingMethod($key, $args = []) { - $method = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))); + $method = 'set' . str_replace('_', '', ucwords($key, '_')); $this->{$method}($args); return $this; } @@ -216,7 +216,7 @@ public function setDataUsingMethod($key, $args = []) */ public function getDataUsingMethod($key, $args = null) { - $method = 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))); + $method = 'get' . str_replace('_', '', ucwords($key, '_')); return $this->{$method}($args); } diff --git a/lib/internal/Magento/Framework/DataObject/Copy.php b/lib/internal/Magento/Framework/DataObject/Copy.php index 8d8896c6cb62a..6a908ae78a343 100644 --- a/lib/internal/Magento/Framework/DataObject/Copy.php +++ b/lib/internal/Magento/Framework/DataObject/Copy.php @@ -239,7 +239,7 @@ protected function _setFieldsetFieldValue($target, $targetCode, $value) */ protected function getAttributeValueFromExtensibleDataObject($source, $code) { - $method = 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $code))); + $method = 'get' . str_replace('_', '', ucwords($code, '_')); $methodExists = method_exists($source, $method); if ($methodExists == true) { @@ -273,7 +273,7 @@ protected function getAttributeValueFromExtensibleDataObject($source, $code) */ protected function setAttributeValueFromExtensibleDataObject($target, $code, $value) { - $method = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $code))); + $method = 'set' . str_replace('_', '', ucwords($code, '_')); $methodExists = method_exists($target, $method); if ($methodExists == true) { diff --git a/lib/internal/Magento/Framework/Module/PackageInfo.php b/lib/internal/Magento/Framework/Module/PackageInfo.php index 0dce507ba26f4..848ad4495f39e 100644 --- a/lib/internal/Magento/Framework/Module/PackageInfo.php +++ b/lib/internal/Magento/Framework/Module/PackageInfo.php @@ -176,8 +176,7 @@ public function getNonExistingDependencies() protected function convertPackageNameToModuleName($packageName) { $moduleName = str_replace('magento/module-', '', $packageName); - $moduleName = str_replace('-', ' ', $moduleName); - $moduleName = str_replace(' ', '', ucwords($moduleName)); + $moduleName = str_replace('-', '', ucwords($moduleName, '-')); return 'Magento_' . $moduleName; } From 93f1caa3623e711b6699107bb3dcb24e982eb750 Mon Sep 17 00:00:00 2001 From: LisovyiEvhenii <lisovyievhenii@gmail.com> Date: Fri, 25 Jan 2019 10:32:37 +0200 Subject: [PATCH 032/254] #13982: fix page title to be displayed both in head's <title> and page's title --- .../view/frontend/layout/customer_account_login.xml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml b/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml index 00f9c4ed84d24..3518df736c4ac 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_account_login.xml @@ -6,12 +6,10 @@ */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <head> + <title>Customer Login + - - - Customer Login - - From 5c7d8994776ce6d902b7ef793afd8fafd21722f1 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Fri, 25 Jan 2019 13:22:21 +0000 Subject: [PATCH 033/254] Changed references to "Store" to "Scope" in framework components. --- .../Sales/Model/Order/Email/SenderBuilder.php | 2 +- .../Model/Order/Email/SenderBuilderTest.php | 8 +++--- .../Mail/Template/TransportBuilder.php | 26 ++++++++++++++----- .../Unit/Template/TransportBuilderTest.php | 8 +++--- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index a7d749ec04c7d..ed9e38822245f 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -106,7 +106,7 @@ protected function configureEmailTemplate() $this->transportBuilder->setTemplateIdentifier($this->templateContainer->getTemplateId()); $this->transportBuilder->setTemplateOptions($this->templateContainer->getTemplateOptions()); $this->transportBuilder->setTemplateVars($this->templateContainer->getTemplateVars()); - $this->transportBuilder->setFromByStore( + $this->transportBuilder->setFromByScope( $this->identityContainer->getEmailIdentity(), $this->identityContainer->getStore()->getId() ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php index 759d60d9e6613..24cd54e3a46b3 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php @@ -76,7 +76,7 @@ protected function setUp() 'setTemplateIdentifier', 'setTemplateOptions', 'setTemplateVars', - 'setFromByStore', + 'setFromByScope', ] ); @@ -103,7 +103,7 @@ protected function setUp() ->method('getEmailIdentity') ->will($this->returnValue($emailIdentity)); $this->transportBuilder->expects($this->once()) - ->method('setFromByStore') + ->method('setFromByScope') ->with($this->equalTo($emailIdentity), 1); $this->identityContainerMock->expects($this->once()) @@ -146,7 +146,7 @@ public function testSend() ->method('getId') ->willReturn(1); $this->transportBuilder->expects($this->once()) - ->method('setFromByStore') + ->method('setFromByScope') ->with($identity, 1); $this->transportBuilder->expects($this->once()) ->method('addTo') @@ -176,7 +176,7 @@ public function testSendCopyTo() ->method('addTo') ->with($this->equalTo('example@mail.com')); $this->transportBuilder->expects($this->once()) - ->method('setFromByStore') + ->method('setFromByScope') ->with($identity, 1); $this->identityContainerMock->expects($this->once()) ->method('getStore') diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index a7bb96122a84d..0d69b3d96cebf 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -177,29 +177,43 @@ public function setReplyTo($email, $name = null) /** * Set mail from address * - * @deprecated This function sets the from address for the first store only. - * new function setFromByStore introduced to allow setting of from address - * based on store. - * @see setFromByStore() + * @deprecated This function sets the from address but does not provide + * a way of setting the correct from addresses based on the scope. + * @see setFromByScope() * * @param string|array $from * @return $this */ public function setFrom($from) { - return $this->setFromByStore($from, null); + return $this->setFromByScope($from, null); } /** * Set mail from address by store * + * @deprecated Use setFromByScope + * @see setFromByScope() + * * @param string|array $from * @param string|int $store * @return $this */ public function setFromByStore($from, $store = null) { - $result = $this->_senderResolver->resolve($from, $store); + return $this->setFromByScope($from, $store); + } + + /** + * Set mail from address by scopeId + * + * @param string|array $from + * @param string|int $scopeId + * @return $this + */ + public function setFromByScope($from, $scopeId = null) + { + $result = $this->_senderResolver->resolve($from, $scopeId); $this->message->setFromAddress($result['email'], $result['name']); return $this; } diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index b476eecd7f59f..5e3309af6497b 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -167,20 +167,20 @@ public function getTransportDataProvider() /** * @return void */ - public function testSetFromByStore() + public function testSetFromByScope() { $sender = ['email' => 'from@example.com', 'name' => 'name']; - $store = 1; + $scopeId = 1; $this->senderResolverMock->expects($this->once()) ->method('resolve') - ->with($sender, $store) + ->with($sender, $scopeId) ->willReturn($sender); $this->messageMock->expects($this->once()) ->method('setFromAddress') ->with($sender['email'], $sender['name']) ->willReturnSelf(); - $this->builder->setFromByStore($sender, $store); + $this->builder->setFromByScope($sender, $scopeId); } /** From 71b24df7f2262f95a01732a2bbb1abb96fe53f23 Mon Sep 17 00:00:00 2001 From: Oleg Onufer Date: Fri, 25 Jan 2019 16:38:57 +0200 Subject: [PATCH 034/254] MAGETWO-97240: Category rules should apply to complex products --- ...tegoryRulesShouldApplyToComplexProductsTest.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml index 52598e98fd563..d8c5b42dbaaaf 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCategoryRulesShouldApplyToComplexProductsTest.xml @@ -31,28 +31,28 @@ - + - + - + - + - + - + - + From a0516563c682a9d3b51ffd0f0aa7040591dedf0a Mon Sep 17 00:00:00 2001 From: Nazar Klovanych Date: Sat, 26 Jan 2019 21:44:12 +0200 Subject: [PATCH 035/254] Update File.php to resolve issue 19983 --- app/code/Magento/Eav/Model/Attribute/Data/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Attribute/Data/File.php b/app/code/Magento/Eav/Model/Attribute/Data/File.php index 8a29c1f2326cf..32283d38a065f 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/File.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/File.php @@ -146,7 +146,7 @@ protected function _validateByRules($value) return $this->_fileValidator->getMessages(); } - if (empty($value['tmp_name']) || !is_uploaded_file($value['tmp_name'])) { + if (!empty($value['tmp_name']) || !file_exists($value['tmp_name'])) { return [__('"%1" is not a valid file.', $label)]; } From 13722031d11aeb6ce92f0272db06714bce7840e0 Mon Sep 17 00:00:00 2001 From: Nazar Date: Sat, 26 Jan 2019 21:45:56 +0200 Subject: [PATCH 036/254] Update File.php --- app/code/Magento/Eav/Model/Attribute/Data/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Attribute/Data/File.php b/app/code/Magento/Eav/Model/Attribute/Data/File.php index 32283d38a065f..a52c88261166e 100644 --- a/app/code/Magento/Eav/Model/Attribute/Data/File.php +++ b/app/code/Magento/Eav/Model/Attribute/Data/File.php @@ -146,7 +146,7 @@ protected function _validateByRules($value) return $this->_fileValidator->getMessages(); } - if (!empty($value['tmp_name']) || !file_exists($value['tmp_name'])) { + if (!empty($value['tmp_name']) && !file_exists($value['tmp_name'])) { return [__('"%1" is not a valid file.', $label)]; } From 4dc983488b0dafa8a67b3cd1866b0bfdbb8c5868 Mon Sep 17 00:00:00 2001 From: DmytroPaidych Date: Tue, 29 Jan 2019 04:22:01 +0200 Subject: [PATCH 037/254] MAGETWO-97212: Enable skipped test - Move Anchored Category with Products (cron is ON, "Update on Save") --- .../LayeredNavigation/Test/Block/Navigation.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/Block/Navigation.php b/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/Block/Navigation.php index 9d2b6a0ed0e7a..97c43d7c4e2ce 100644 --- a/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/Block/Navigation.php +++ b/dev/tests/functional/tests/app/Magento/LayeredNavigation/Test/Block/Navigation.php @@ -131,16 +131,17 @@ public function applyFilter($filter, $linkPattern) */ public function isCategoryVisible(Category $category, $qty) { - $link = $this->_rootElement->find(sprintf($this->optionTitle, 'Category'), Locator::SELECTOR_XPATH); - if ($link->isVisible()) { - $link->click(); + $link = sprintf($this->categoryName, $category->getName()); + + if (!$this->_rootElement->find($link, Locator::SELECTOR_XPATH)->isVisible()) { + $this->openFilterContainer('Category', $link); return $this->_rootElement->find( - sprintf($this->categoryName, $category->getName()) . sprintf($this->productQtyInCategory, $qty), + $link . sprintf($this->productQtyInCategory, $qty), Locator::SELECTOR_XPATH )->isVisible(); } else { return $this->_rootElement->find( - sprintf($this->categoryName, $category->getName()) . sprintf($this->productQty, $qty), + $link . sprintf($this->productQty, $qty), Locator::SELECTOR_XPATH )->isVisible(); } From e907219ec470321c1638d4ff69935e08e8b450ab Mon Sep 17 00:00:00 2001 From: DmytroPaidych Date: Wed, 30 Jan 2019 06:07:42 +0200 Subject: [PATCH 038/254] MAGETWO-97237: Delete Product Staging Update when the Product is used --- .../Test/Mftf/Data/ProductLinkData.xml | 19 +++++++++++++++++++ .../Test/Mftf/Data/ProductLinksData.xml | 14 ++++++++++++++ .../AdminCreateWidgetActionGroup.xml | 12 +++--------- .../Widget/Test/Mftf/Data/WidgetsData.xml | 1 - .../Test/Mftf/Page/AdminNewWidgetPage.xml | 1 + 5 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/ProductLinkData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/ProductLinksData.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinkData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinkData.xml new file mode 100644 index 0000000000000..000bb2095002c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinkData.xml @@ -0,0 +1,19 @@ + + + + + + + + related + simple + 1 + Qty1000 + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinksData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinksData.xml new file mode 100644 index 0000000000000..bd4f807880ab8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductLinksData.xml @@ -0,0 +1,14 @@ + + + + + + RelatedProductLink + + diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml index 2dcfc4e99333a..969ab58b04876 100644 --- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml +++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml @@ -65,23 +65,17 @@ - + - - - - - - - - + + diff --git a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml index 6773dee7cfaa9..1ce16a01fab1d 100644 --- a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml +++ b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml @@ -33,7 +33,6 @@ Header - Catalog Product Link Product Link Block Template diff --git a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml index d495a36f68d0a..2e08c239c810a 100644 --- a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml +++ b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd">
+
From d7ba787a107ec5599dd60af84ca99fa73fff6b40 Mon Sep 17 00:00:00 2001 From: Arvinda kumar Date: Thu, 31 Jan 2019 12:07:12 +0530 Subject: [PATCH 039/254] _fields.less updated _fields.less updated --- .../adminhtml/Magento/backend/web/css/source/forms/_fields.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 81a55443d6a3c..abef32dc48cd0 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 @@ -531,7 +531,6 @@ & > .admin__field { &:first-child { position: static; - width: 100%; & > .admin__field-label { #mix-grid .column(@field-label-grid__column, @field-grid__columns); cursor: pointer; @@ -685,6 +684,7 @@ margin: 0; opacity: 1; position: static; + width: 100%; } } & > .admin__field-label { From e25e3ddf94ea1b1726aa17dd3eb90503714325dc Mon Sep 17 00:00:00 2001 From: DmytroPaidych Date: Thu, 31 Jan 2019 14:01:35 +0200 Subject: [PATCH 040/254] MAGETWO-97237: Delete Product Staging Update when the Product is used --- .../Catalog/Test/Mftf/Data/WidgetsData.xml | 15 +++++++++++++++ .../Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml | 14 ++++++++++++++ .../Test/Mftf/Section/AdminNewWidgetSection.xml | 14 ++++++++++++++ .../AdminNewWidgetSelectProductPopupSection.xml | 0 .../Magento/Widget/Test/Mftf/Data/WidgetsData.xml | 4 ---- .../Widget/Test/Mftf/Page/AdminNewWidgetPage.xml | 1 - .../Test/Mftf/Section/AdminNewWidgetSection.xml | 1 - 7 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml rename app/code/Magento/{Widget => Catalog}/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml (100%) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml new file mode 100644 index 0000000000000..83f0a56c21545 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/WidgetsData.xml @@ -0,0 +1,15 @@ + + + + + + Catalog Product Link + Product Link Block Template + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml new file mode 100644 index 0000000000000..f645c6cd54278 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml @@ -0,0 +1,14 @@ + + + + + +
+ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml new file mode 100644 index 0000000000000..5329ad48c8f43 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -0,0 +1,14 @@ + + + + +
+ +
+
diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml similarity index 100% rename from app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml rename to app/code/Magento/Catalog/Test/Mftf/Section/AdminNewWidgetSelectProductPopupSection.xml diff --git a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml index 1ce16a01fab1d..27222298408de 100644 --- a/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml +++ b/app/code/Magento/Widget/Test/Mftf/Data/WidgetsData.xml @@ -32,8 +32,4 @@ Cart Price Rule Related Header - - Catalog Product Link - Product Link Block Template - diff --git a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml index 2e08c239c810a..d495a36f68d0a 100644 --- a/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml +++ b/app/code/Magento/Widget/Test/Mftf/Page/AdminNewWidgetPage.xml @@ -10,6 +10,5 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd">
-
diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml index 22f8a1e0fe981..8cf7fcfa2ec6c 100644 --- a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml +++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml @@ -29,6 +29,5 @@ -
From 366b06103238a145460e9128532951d6517fe30b Mon Sep 17 00:00:00 2001 From: DmytroPaidych Date: Thu, 31 Jan 2019 15:15:02 +0200 Subject: [PATCH 041/254] MAGETWO-97237: Delete Product Staging Update when the Product is used --- app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml index f645c6cd54278..e23a503266e33 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminNewWidgetPage.xml @@ -8,7 +8,7 @@ - +
From 4bb06ef6bccb10c881d7803350060a177bfd60b9 Mon Sep 17 00:00:00 2001 From: Dipti 2Jcommerce Date: Thu, 31 Jan 2019 19:00:59 +0530 Subject: [PATCH 042/254] ipad-view-order-summary-block --- .../web/css/source/module/checkout/_order-summary.less | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less index 9bad9518f5724..85a960e8bc05a 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less @@ -148,14 +148,14 @@ } .product-item-name-block { - display: table-cell; + display: block; padding-right: @indent__xs; text-align: left; } .subtotal { - display: table-cell; - text-align: right; + display: block; + text-align: left; } .price { From 99f260a14f48b56eaf8185f7c926fe01cfcd1fd7 Mon Sep 17 00:00:00 2001 From: Ilan Parmentier Date: Thu, 31 Jan 2019 14:44:29 +0100 Subject: [PATCH 043/254] Update details.phtml Good afternoon, I just encountered a problem with product's tabs in product info. I realized when description or other custom tabs have links, when you click on it, nothing happen. It caused by the script mage/tabs.js in details.phtml. The current link to trigger the tab is declared with data-role="switch" instead of a data-role="trigger". https://github.com/magento/magento2/blob/4f232511ceba6f1f7bf6f73b3b5609bd087f8c74/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml#L24-L37 When not even the trigger and the header is declared respectively by data-role="heading" and data-role="trigger", the script based it search on the current collapsible panel declared by data-role="collapsible". https://github.com/magento/magento2/blob/4f232511ceba6f1f7bf6f73b3b5609bd087f8c74/lib/web/mage/tabs.js#L99-L124 You can simply try by adding a link in the product description. Tell me if I am wrong. Cheers, Ilan PARMENTIER --- .../Catalog/view/frontend/templates/product/view/details.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml index af664051b1431..003ae6b0c3844 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml @@ -26,7 +26,7 @@ data-role="collapsible" id="tab-label-"> From d4027d61b968999d77c194c5517b19465af8ba55 Mon Sep 17 00:00:00 2001 From: Ravi Chandra Date: Fri, 1 Feb 2019 18:05:35 +0530 Subject: [PATCH 044/254] Correct spelling --- app/code/Magento/Ui/view/base/web/js/grid/data-storage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dad67da3ea8ad..547cdab16cdf1 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 @@ -199,7 +199,7 @@ define([ }, /** - * Caches requests object with provdided parameters + * Caches requests object with provided parameters * and data object associated with it. * * @param {Object} data - Data associated with request. From 60c0c10078a7d9db87603ffc6996e37c1ea4e8ad Mon Sep 17 00:00:00 2001 From: Ravi Chandra Date: Fri, 1 Feb 2019 18:09:51 +0530 Subject: [PATCH 045/254] Correct spelling --- app/code/Magento/Ui/view/base/web/js/lib/view/utils/bindings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/bindings.js b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/bindings.js index 77de8a1ceb0ed..48515b668f80d 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/bindings.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/bindings.js @@ -89,7 +89,7 @@ define([ /** * Adds specified bindings to each DOM element in - * collection and evalutes them with provided context. + * collection and evaluates them with provided context. * * @param {(Object|Function)} data - Either bindings object or a function * which returns bindings data for each element in collection. From d5729beaf9f62ce9599de191d829a019edcf675f Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin Date: Mon, 4 Feb 2019 09:47:40 +0200 Subject: [PATCH 046/254] MAGETWO-97968: Minicart isn't updated for disabled products --- .../frontend/templates/cart/noItems.phtml | 7 ++++ .../view/frontend/web/js/empty-cart.js | 12 ++++++ ...efrontGuestCheckoutDisabledProductTest.xml | 42 +++++++++++++++++-- 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml index 1c0c221a550cd..67ac4a9335565 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml @@ -13,3 +13,10 @@ $block->escapeUrl($block->getContinueShoppingUrl())) ?>

getChildHtml('shopping.cart.table.after') ?> + \ No newline at end of file diff --git a/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js new file mode 100644 index 0000000000000..27d38697afe39 --- /dev/null +++ b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js @@ -0,0 +1,12 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Customer/js/customer-data' +], function (customerData) { + 'use strict'; + + customerData.reload(['cart'], false); +}); diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml index ab0db2dac643e..456474bbc28cf 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -22,6 +22,9 @@ + + + @@ -73,13 +76,14 @@ + - + @@ -97,7 +101,6 @@ - @@ -114,10 +117,41 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 92d4ffadeb479344c01ad35d8fb5149cb545d7c0 Mon Sep 17 00:00:00 2001 From: Dipti 2Jcommerce Date: Mon, 4 Feb 2019 15:21:34 +0530 Subject: [PATCH 047/254] ipad-view-order-summary-block --- .../module/checkout/_order-summary.less | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less index 85a960e8bc05a..ae21a3877c350 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less @@ -227,3 +227,27 @@ } } } + +// +// Tablet +// _____________________________________________ + +@media only screen and (max-width: @screen__m) { + .opc-block-summary { + .product-item { + .product-item-inner { + display: block; + } + + .product-item-name-block { + display: block; + text-align: left; + } + + .subtotal { + display: block; + text-align: left; + } + } + } +} \ No newline at end of file From 022e8eb3a1e0a86a626bc1a41cefcad53181b3a3 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin Date: Mon, 4 Feb 2019 16:27:59 +0200 Subject: [PATCH 048/254] MAGETWO-97968: Minicart isn't updated for disabled products --- .../Test/StorefrontGuestCheckoutDisabledProductTest.xml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml index 456474bbc28cf..29715d073d01b 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -75,6 +75,8 @@ + + @@ -85,13 +87,11 @@ - - @@ -100,7 +100,6 @@ - @@ -124,22 +123,18 @@ - - - - From a18f3b67a2c7c58368ea72f97802c26b67c56907 Mon Sep 17 00:00:00 2001 From: Kevin Kozan Date: Mon, 4 Feb 2019 11:34:47 -0600 Subject: [PATCH 049/254] MC-4416: Convert CreateProductAttributeEntityTest to MFTF - Variation MC-10894 --- .../AdminProductAttributeActionGroup.xml | 17 ++++++ .../Test/Mftf/Data/ProductAttributeData.xml | 5 ++ .../AdminCreateProductAttributeSection.xml | 8 +++ .../AdminProductAttributeGridSection.xml | 2 +- .../Test/CreateProductAttributeEntityTest.xml | 53 +++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 80cadbb6571f2..f998108c890d8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -47,4 +47,21 @@ + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index b367cdcab9d8b..4c106b6e69226 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -115,4 +115,9 @@ true ProductAttributeFrontendLabel + + text + defaultValue + No + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 05be20b14acc0..72a0639403501 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -24,6 +24,10 @@
+
+ + +
@@ -77,6 +81,10 @@ + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml index 160948f8f1f2c..d63198e1556d6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
- + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml new file mode 100644 index 0000000000000..7449b6e473e2f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + <description value="Admin should be able to create a TextField product attribute"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10894"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="navigateToEditProductAttribute" 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"/> + </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="createProductAttributeWithTextField" stepKey="key1"> + <argument name="attribute" value="textProductAttribute"/> + </actionGroup> + + <!--Save Product Attribute.--> + <actionGroup ref="navigateToEditProductAttribute" 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}}"/> + </test> +</tests> From cc68f37e80688b5b74cde97267ce4eb91ef208c0 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Tue, 5 Feb 2019 10:20:14 -0600 Subject: [PATCH 050/254] MC-4379: Convert SubcategoryNotIncludeInNavigationMenuTest to MFTF --- .../Catalog/Test/Mftf/Data/CategoryData.xml | 6 +++ ...ubcategoryIsNotVisibleInNavigationTest.xml | 53 +++++++++++++++++++ ...tegoryIsNotVisibleInNavigationMenuTest.xml | 52 ++++++++++++++++++ ...ubcategoryIsNotVisibleInNavigationTest.xml | 53 +++++++++++++++++++ ...tegoryIsNotVisibleInNavigationMenuTest.xml | 52 ++++++++++++++++++ 5 files changed, 216 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml index a11c3fd0d7afa..27167d03d528e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CategoryData.xml @@ -104,4 +104,10 @@ <data key="is_active">false</data> <data key="include_in_menu">true</data> </entity> + <entity name="CatInactiveNotInMenu" type="category"> + <data key="name" unique="suffix">InactiveNotInMenu</data> + <data key="name_lwr" unique="suffix">inactivenotinmenu</data> + <data key="is_active">false</data> + <data key="include_in_menu">false</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml new file mode 100644 index 0000000000000..d6126916046a9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.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="AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest"> + <annotations> + <stories value="Create category"/> + <title value="Inactive Category and subcategory are not visible on navigation menu, Include in Menu = No"/> + <description value="Login as admin and verify inactive and inactive include in menu category and subcategory is not visible in navigation menu"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13638"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!--Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <!--Create Parent Inactive and Not Include In Menu Category --> + <createData entity="CatInactiveNotInMenu" stepKey="createCategory"/> + </before> + + <after> + <!--Delete created data--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Open Category Page--> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForPageToLoaded"/> + <!--Create subcategory under parent category --> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> + <waitForPageLoad stepKey="waitForCategoryToLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatInactiveNotInMenu.name)}}" stepKey="selectCategory"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="enableCategory"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="enableIncludeInMenu"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="addSubCategoryName"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveSubCategory"/> + <waitForPageLoad stepKey="waitForSecondCategoryToSave"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> + <!-- Verify Parent Category and Sub category is not visible in navigation menu --> + <amOnPage url="{{CatInactiveNotInMenu.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> + <waitForPageLoad stepKey="waitForCategoryStoreFrontPageToLoad"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(CatInactiveNotInMenu.name)}}" stepKey="dontSeeCategoryOnStoreNavigationBar"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSubCategoryOnStoreNavigation"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml new file mode 100644 index 0000000000000..9362719ba56de --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.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="AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest"> + <annotations> + <stories value="Create category"/> + <title value="Inactive Category and subcategory are not visible on navigation menu, Include in Menu = Yes"/> + <description value="Login as admin and verify inactive category and subcategory is not visible in navigation menu"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13637"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!--Login as Admin --> + <actionGroup ref="LoginAsAdmin" 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"/> + </after> + <!--Open Category Page--> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForPageToLoaded"/> + <!--Create subcategory under parent category --> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> + <waitForPageLoad stepKey="waitForCategoryToLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatNotActive.name)}}" stepKey="selectCategory"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="enableCategory"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="enableIncludeInMenu"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="addSubCategoryName"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveSubCategory"/> + <waitForPageLoad stepKey="waitForSecondCategoryToSave"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> + <!-- Verify Parent Category and Sub category is not visible in navigation menu --> + <amOnPage url="{{CatNotActive.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> + <waitForPageLoad stepKey="waitForCategoryStoreFrontPageToLoad"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(CatNotActive.name)}}" stepKey="dontSeeCategoryOnStoreNavigationBar"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSubCategoryOnStoreNavigation"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml new file mode 100644 index 0000000000000..2b8f47d1ca265 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.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="AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest"> + <annotations> + <stories value="Create category"/> + <title value="Active Category and subcategory are not visible on navigation menu, Include in Menu = No"/> + <description value="Login as admin and verify inactive include in menu category and subcategory is not visible in navigation menu"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13636"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!--Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <!--Create inactive Include In Menu Parent Category --> + <createData entity="CatNotIncludeInMenu" stepKey="createCategory"/> + </before> + + <after> + <!--Delete created data--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Open Category Page--> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForPageToLoaded"/> + <!--Create subcategory under parent category --> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> + <waitForPageLoad stepKey="waitForCategoryToLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatNotIncludeInMenu.name)}}" stepKey="selectCategory"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="addSubCategoryName"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="enableCategory"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="enableIncludeInMenu"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveSubCategory"/> + <waitForPageLoad stepKey="waitForSecondCategoryToSave"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> + <!-- Verify Parent Category and Sub category is not visible in navigation menu --> + <amOnPage url="{{CatNotIncludeInMenu.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> + <waitForPageLoad stepKey="waitForCategoryStoreFrontPageToLoad"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(CatNotIncludeInMenu.name)}}" stepKey="dontSeeCategoryOnStoreNavigationBar"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSubCategoryOnStoreNavigation"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml new file mode 100644 index 0000000000000..e250fa7b15dd1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.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="AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest"> + <annotations> + <stories value="Create category"/> + <title value="Active category is visible on navigation menu while subcategory is not visible on navigation menu, Include in Menu = Yes"/> + <description value="Login as admin and verify subcategory is not visible in navigation menu"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13635"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!--Login as Admin --> + <actionGroup ref="LoginAsAdmin" 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"/> + </after> + <!--Open Category Page--> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForPageToLoaded"/> + <!--Create subcategory under parent category --> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> + <waitForPageLoad stepKey="waitForCategoryToLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(_defaultCategory.name)}}" stepKey="selectCategory"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="addSubCategoryName"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="enableCategory"/> + <checkOption selector="{{AdminCategoryBasicFieldSection.IncludeInMenu}}" stepKey="enableIncludeInMenu"/> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveSubCategory"/> + <waitForPageLoad stepKey="waitForSecondCategoryToSave"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> + <!-- Verify Parent Category is visible in navigation menu and Sub category is not visible in navigation menu --> + <amOnPage url="{{_defaultCategory.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> + <waitForPageLoad stepKey="waitForCategoryStoreFrontPageToLoad"/> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(_defaultCategory.name)}}" stepKey="seeCategoryOnStoreNavigationBar"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSubCategoryOnStoreNavigation"/> + </test> +</tests> From 7b9c6058e8462165dd975ab9d005f671d29a5492 Mon Sep 17 00:00:00 2001 From: Alexandre Jardin <info@ajardin.fr> Date: Tue, 5 Feb 2019 15:11:29 +0100 Subject: [PATCH 051/254] Make the module list more deterministic --- .../Framework/Module/ModuleList/Loader.php | 36 +++++++++++-- .../Test/Unit/ModuleList/LoaderTest.php | 51 +++++++++++++++++++ 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php index bdfb77762b41c..80958f65e110e 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php +++ b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php @@ -126,16 +126,21 @@ private function getModuleConfigs() * * @param array $origList * @return array - * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @throws \Exception */ - private function sortBySequence($origList) + private function sortBySequence(array $origList): array { ksort($origList); + $modules = $this->prearrangeModules($origList); + $expanded = []; - foreach ($origList as $moduleName => $value) { + foreach ($modules as $moduleName => $value) { + $sequence = $this->expandSequence($origList, $moduleName); + asort($sequence); + $expanded[] = [ 'name' => $moduleName, - 'sequence' => $this->expandSequence($origList, $moduleName), + 'sequence' => $sequence, ]; } @@ -143,7 +148,7 @@ private function sortBySequence($origList) $total = count($expanded); for ($i = 0; $i < $total - 1; $i++) { for ($j = $i; $j < $total; $j++) { - if (in_array($expanded[$j]['name'], $expanded[$i]['sequence'])) { + if (in_array($expanded[$j]['name'], $expanded[$i]['sequence'], true)) { $temp = $expanded[$i]; $expanded[$i] = $expanded[$j]; $expanded[$j] = $temp; @@ -159,6 +164,27 @@ private function sortBySequence($origList) return $result; } + /** + * Prearrange all modules by putting those from Magento before the others + * + * @param array $modules + * @return array + */ + private function prearrangeModules(array $modules): array + { + $breakdown = ['magento' => [], 'others' => []]; + + foreach ($modules as $moduleName => $moduleDetails) { + if (strpos($moduleName, 'Magento_') !== false) { + $breakdown['magento'][$moduleName] = $moduleDetails; + } else { + $breakdown['others'][$moduleName] = $moduleDetails; + } + } + + return array_merge($breakdown['magento'], $breakdown['others']); + } + /** * Accumulate information about all transitive "sequence" references * diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php index fe613450fd485..a62bb5fa70f97 100644 --- a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php +++ b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleList/LoaderTest.php @@ -160,4 +160,55 @@ public function testLoadCircular() ])); $this->loader->load(); } + + /** + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testLoadPrearranged(): void + { + $fixtures = [ + 'Foo_Bar' => ['name' => 'Foo_Bar', 'sequence' => ['Magento_Store']], + 'Magento_Directory' => ['name' => 'Magento_Directory', 'sequence' => ['Magento_Store']], + 'Magento_Store' => ['name' => 'Magento_Store', 'sequence' => []], + 'Magento_Theme' => ['name' => 'Magento_Theme', 'sequence' => ['Magento_Store', 'Magento_Directory']], + 'Test_HelloWorld' => ['name' => 'Test_HelloWorld', 'sequence' => ['Magento_Theme']] + ]; + + $index = 0; + foreach ($fixtures as $name => $fixture) { + $this->converter->expects($this->at($index++))->method('convert')->willReturn([$name => $fixture]); + } + + $this->registry->expects($this->once()) + ->method('getPaths') + ->willReturn([ + '/path/to/Foo_Bar', + '/path/to/Magento_Directory', + '/path/to/Magento_Store', + '/path/to/Magento_Theme', + '/path/to/Test_HelloWorld' + ]); + + $this->driver->expects($this->exactly(5)) + ->method('fileGetContents') + ->will($this->returnValueMap([ + ['/path/to/Foo_Bar/etc/module.xml', null, null, self::$sampleXml], + ['/path/to/Magento_Directory/etc/module.xml', null, null, self::$sampleXml], + ['/path/to/Magento_Store/etc/module.xml', null, null, self::$sampleXml], + ['/path/to/Magento_Theme/etc/module.xml', null, null, self::$sampleXml], + ['/path/to/Test_HelloWorld/etc/module.xml', null, null, self::$sampleXml], + ])); + + // Load the full module list information + $result = $this->loader->load(); + + $this->assertSame( + ['Magento_Store', 'Magento_Directory', 'Magento_Theme', 'Foo_Bar', 'Test_HelloWorld'], + array_keys($result) + ); + + foreach ($fixtures as $name => $fixture) { + $this->assertSame($fixture, $result[$name]); + } + } } From a51df0ab6ef6433f5cb25c30f3d753c2c40bd2a3 Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 6 Feb 2019 11:49:25 -0600 Subject: [PATCH 052/254] MC-4416: Convert CreateProductAttributeEntityTest to MFTF - Added 3 variations - Added asserts to first variation --- .../AdminProductAttributeActionGroup.xml | 41 +++- .../Test/Mftf/Data/ProductAttributeData.xml | 19 ++ .../Test/Mftf/Page/AdminProductCreatePage.xml | 2 + .../AdminCreateProductAttributeSection.xml | 6 + .../AdminProductAddAttributeModalSection.xml | 19 ++ .../Section/AdminProductAttributesSection.xml | 17 ++ .../Section/AdminProductFormActionSection.xml | 1 + .../Test/CreateProductAttributeEntityTest.xml | 209 +++++++++++++++++- 8 files changed, 311 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAddAttributeModalSection.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributesSection.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index f998108c890d8..89321f8f6506a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -47,6 +47,20 @@ <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> </actionGroup> + <!--Clicks Add Attribute and adds the given attribute--> + <actionGroup name="addProductAttributeInProductModal"> + <arguments> + <argument name="attributeCode" type="string"/> + </arguments> + <click stepKey="addAttribute" selector="{{AdminProductFormActionSection.addAttributeButton}}"/> + <conditionalClick selector="{{AdminProductAddAttributeModalSection.clearFilters}}" dependentSelector="{{AdminProductAddAttributeModalSection.clearFilters}}" visible="true" stepKey="clearFilters"/> + <click stepKey="clickFilters" selector="{{AdminProductAddAttributeModalSection.filters}}"/> + <fillField stepKey="fillCode" selector="{{AdminProductAddAttributeModalSection.attributeCodeFilter}}" userInput="{{attributeCode}}"/> + <click stepKey="clickApply" selector="{{AdminProductAddAttributeModalSection.applyFilters}}"/> + <waitForPageLoad stepKey="waitForFilters"/> + <checkOption selector="{{AdminProductAddAttributeModalSection.firstRowCheckBox}}" stepKey="checkAttribute"/> + <click stepKey="addSelected" selector="{{AdminProductAddAttributeModalSection.addSelected}}"/> + </actionGroup> <!--Clicks createNewAttribute and fills out form--> <actionGroup name="createProductAttribute"> <arguments> @@ -58,10 +72,35 @@ <selectOption selector="{{AttributePropertiesSection.ValueRequired}}" stepKey="checkRequired" userInput="{{attribute.is_required_admin}}"/> <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> </actionGroup> - <!-- Inputs default value and attribute code--> + <!-- Inputs text default value and attribute code--> <actionGroup name="createProductAttributeWithTextField" extends="createProductAttribute" insertAfter="checkRequired"> <click stepKey="openAdvancedProperties" selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}"/> <fillField stepKey="fillCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attribute.attribute_code}}"/> <fillField stepKey="fillDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueText}}" userInput="{{attribute.default_value}}"/> </actionGroup> + <!-- Inputs date default value and attribute code--> + <actionGroup name="createProductAttributeWithDateField" extends="createProductAttribute" insertAfter="checkRequired"> + <arguments> + <argument name="date" type="string"/> + </arguments> + <click stepKey="openAdvancedProperties" selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}"/> + <fillField stepKey="fillCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attribute.attribute_code}}"/> + <fillField stepKey="fillDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueDate}}" userInput="{{date}}"/> + </actionGroup> + <!-- Creates dropdown option at row without saving--> + <actionGroup name="createAttributeDropdownNthOption"> + <arguments> + <argument name="row" type="string"/> + <argument name="adminName" type="string"/> + <argument name="frontName" type="string"/> + </arguments> + <click stepKey="clickAddOptions" selector="{{AttributePropertiesSection.dropdownAddOptions}}"/> + <waitForPageLoad stepKey="waitForNewOption"/> + <fillField stepKey="fillAdmin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin(row)}}" userInput="{{adminName}}"/> + <fillField stepKey="fillStoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView(row)}}" userInput="{{frontName}}"/> + </actionGroup> + <!-- Creates dropdown option at row as default--> + <actionGroup name="createAttributeDropdownNthOptionAsDefault" extends="createAttributeDropdownNthOption"> + <checkOption selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault(row)}}" stepKey="setAsDefault" after="fillStoreView"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index 4c106b6e69226..b31a3cc859221 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -120,4 +120,23 @@ <data key="default_value" unique="suffix">defaultValue</data> <data key="is_required_admin">No</data> </entity> + <entity name="dateProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> + <data key="frontend_input">date</data> + <data key="is_required_admin">No</data> + </entity> + <entity name="priceProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> + <data key="frontend_input">date</data> + <data key="is_required_admin">No</data> + </entity> + <entity name="dropdownProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> + <data key="frontend_input">select</data> + <data key="frontend_input_admin">Dropdown</data> + <data key="is_required_admin">No</data> + <data key="option1_admin" unique="suffix">opt1Admin</data> + <data key="option1_frontend" unique="suffix">opt1Front</data> + <data key="option2_admin" unique="suffix">opt2Admin</data> + <data key="option2_frontend" unique="suffix">opt2Front</data> + <data key="option3_admin" unique="suffix">opt3Admin</data> + <data key="option3_frontend" unique="suffix">opt3Front</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml index b3ed3f478f810..e4c4ece5ac6cf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminProductCreatePage.xml @@ -15,8 +15,10 @@ <section name="AdminProductImagesSection"/> <section name="AdminAddProductsToOptionPanel"/> <section name="AdminProductMessagesSection"/> + <section name="AdminProductAttributesSection"/> <section name="AdminProductFormRelatedUpSellCrossSellSection"/> <section name="AdminProductFormAdvancedPricingSection"/> <section name="AdminProductFormAdvancedInventorySection"/> + <section name="AdminAddAttributeModalSection"/> </page> </pages> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 72a0639403501..5f083171f97cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -23,6 +23,12 @@ <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"/> + <!-- 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 button.action-accept"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAddAttributeModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAddAttributeModalSection.xml new file mode 100644 index 0000000000000..7962e8117a8be --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAddAttributeModalSection.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="AdminProductAddAttributeModalSection"> + <element name="addSelected" type="button" selector=".product_form_product_form_add_attribute_modal .page-main-actions button.action-primary" timeout="30"/> + <element name="filters" type="button" selector=".product_form_product_form_add_attribute_modal button[data-action='grid-filter-expand']" timeout="30"/> + <element name="attributeCodeFilter" type="textarea" selector=".product_form_product_form_add_attribute_modal input[name='attribute_code']"/> + <element name="clearFilters" type="button" selector=".product_form_product_form_add_attribute_modal button.action-clear" timeout="30"/> + <element name="firstRowCheckBox" type="input" selector=".product_form_product_form_add_attribute_modal .data-grid-checkbox-cell input"/> + <element name="applyFilters" type="button" selector=".product_form_product_form_add_attribute_modal .admin__data-grid-filters-footer button.action-secondary" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributesSection.xml new file mode 100644 index 0000000000000..46a516b538f09 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributesSection.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="AdminProductAttributesSection"> + <element name="sectionHeader" type="button" selector="div[data-index='attributes']" timeout="30"/> + <element name="attributeLabelByCode" type="text" selector="div[data-index='{{var}}'] .admin__field-label span" parameterized="true"/> + <element name="attributeTextInputByCode" type="text" selector="div[data-index='{{var}}'] .admin__field-control input" parameterized="true"/> + <element name="attributeDropdownByCode" type="text" selector="div[data-index='{{var}}'] .admin__field-control select" parameterized="true"/> + </section> +</sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml index aa752e0e2289c..1652546b0acb3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormActionSection.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductFormActionSection"> <element name="backButton" type="button" selector="#back" timeout="30"/> + <element name="addAttributeButton" type="button" selector="#addAttribute" timeout="30"/> <element name="saveButton" type="button" selector="#save-button" timeout="30"/> <element name="saveArrow" type="button" selector="button[data-ui-id='save-button-dropdown']" timeout="30"/> <element name="saveAndClose" type="button" selector="span[title='Save & Close']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml index 7449b6e473e2f..fd878e7f87fe4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml @@ -34,11 +34,11 @@ <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> <!--Create new Product Attribute as TextField, with code and default value.--> - <actionGroup ref="createProductAttributeWithTextField" stepKey="key1"> + <actionGroup ref="createProductAttributeWithTextField" stepKey="createAttribute"> <argument name="attribute" value="textProductAttribute"/> </actionGroup> - <!--Save Product Attribute.--> + <!--Navigate to Product Attribute.--> <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage"> <argument name="ProductAttribute" value="{{textProductAttribute.attribute_code}}"/> </actionGroup> @@ -49,5 +49,210 @@ <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="addProductAttributeInProductModal" 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> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="navigateToEditProductAttribute" 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"/> + </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="createProductAttributeWithDateField" stepKey="createAttribute"> + <argument name="attribute" value="dateProductAttribute"/> + <argument name="date" value="{$generateDefaultDate}"/> + </actionGroup> + + <!--Navigate to Product Attribute.--> + <actionGroup ref="navigateToEditProductAttribute" 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="addProductAttributeInProductModal" 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"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="navigateToEditProductAttribute" 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"/> + </after> + + <!--Navigate to Stores > Attributes > Product.--> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="goToProductAttributes"/> + + <!--Create new Product Attribute with Price--> + <actionGroup ref="createProductAttribute" stepKey="createAttribute"> + <argument name="attribute" value="priceProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute.--> + <actionGroup ref="navigateToEditProductAttribute" 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="addProductAttributeInProductModal" 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-10894"/> + <group value="Catalog"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="navigateToEditProductAttribute" 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"/> + </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="createProductAttribute" stepKey="createAttribute"> + <argument name="attribute" value="dropdownProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute, add Product Options and Save - 1--> + <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage1"> + <argument name="ProductAttribute" value="{{dropdownProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="createAttributeDropdownNthOption" stepKey="createOption1"> + <argument name="adminName" value="{{dropdownProductAttribute.option1_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttribute.option1_frontend}}"/> + <argument name="row" value="1"/> + </actionGroup> + <actionGroup ref="createAttributeDropdownNthOption" stepKey="createOption2"> + <argument name="adminName" value="{{dropdownProductAttribute.option2_admin}}"/> + <argument name="frontName" value="{{dropdownProductAttribute.option2_frontend}}"/> + <argument name="row" value="2"/> + </actionGroup> + <actionGroup ref="createAttributeDropdownNthOptionAsDefault" 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="navigateToEditProductAttribute" 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="addProductAttributeInProductModal" 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_frontend}}" stepKey="seeDefaultIsCorrect"/> + <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option1_frontend}}"/> + <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option2_frontend}}"/> + <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_frontend}}"/> </test> </tests> From f77e404aabf6c7e5e20db61e50116f85c38bc12f Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Wed, 6 Feb 2019 13:39:32 -0600 Subject: [PATCH 053/254] MC-4416: Convert CreateProductAttributeEntityTest to MFTF - Added 2 variations --- .../Test/Mftf/Data/ProductAttributeData.xml | 18 +++ .../Test/CreateProductAttributeEntityTest.xml | 151 ++++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index b31a3cc859221..82c96b8049c2e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -139,4 +139,22 @@ <data key="option3_admin" unique="suffix">opt3Admin</data> <data key="option3_frontend" unique="suffix">opt3Front</data> </entity> + <entity name="multiselectProductAttribute" extends="productAttributeWysiwyg" type="ProductAttribute"> + <data key="frontend_input">multiselect</data> + <data key="frontend_input_admin">Multiple Select</data> + <data key="is_required_admin">No</data> + <data key="option1_admin" unique="suffix">opt1Admin</data> + <data key="option1_frontend" unique="suffix">opt1Front</data> + <data key="option2_admin" unique="suffix">opt2Admin</data> + <data key="option2_frontend" unique="suffix">opt2Front</data> + <data key="option3_admin" unique="suffix">opt3Admin</data> + <data key="option3_frontend" unique="suffix">opt3Front</data> + </entity> + <entity name="dropdownProductAttributeWithQuote" extends="productAttributeWysiwyg" type="ProductAttribute"> + <data key="frontend_input">select</data> + <data key="frontend_input_admin">Dropdown</data> + <data key="is_required_admin">No</data> + <data key="option1_admin" unique="suffix">opt1'Admin</data> + <data key="option1_frontend" unique="suffix">opt1'Front</data> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml index fd878e7f87fe4..551d7af1d6a30 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml @@ -255,4 +255,155 @@ <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option2_frontend}}"/> <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttribute.attribute_code)}}" userInput="{{dropdownProductAttribute.option3_frontend}}"/> </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"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="navigateToEditProductAttribute" 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"/> + </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="createProductAttribute" stepKey="createAttribute"> + <argument name="attribute" value="dropdownProductAttributeWithQuote"/> + </actionGroup> + + <!--Navigate to Product Attribute, add Product Option and Save - 1--> + <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage1"> + <argument name="ProductAttribute" value="{{dropdownProductAttributeWithQuote.attribute_code}}"/> + </actionGroup> + <actionGroup ref="createAttributeDropdownNthOptionAsDefault" 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="navigateToEditProductAttribute" 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="addProductAttributeInProductModal" 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_frontend}}" stepKey="seeDefaultIsCorrect"/> + <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(dropdownProductAttributeWithQuote.attribute_code)}}" userInput="{{dropdownProductAttributeWithQuote.option1_frontend}}"/> + </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"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="navigateToEditProductAttribute" 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"/> + </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="createProductAttribute" stepKey="createAttribute"> + <argument name="attribute" value="multiselectProductAttribute"/> + </actionGroup> + + <!--Navigate to Product Attribute, add Product Options and Save - 1--> + <actionGroup ref="navigateToEditProductAttribute" stepKey="goToEditPage1"> + <argument name="ProductAttribute" value="{{multiselectProductAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="createAttributeDropdownNthOption" stepKey="createOption1"> + <argument name="adminName" value="{{multiselectProductAttribute.option1_admin}}"/> + <argument name="frontName" value="{{multiselectProductAttribute.option1_frontend}}"/> + <argument name="row" value="1"/> + </actionGroup> + <actionGroup ref="createAttributeDropdownNthOption" stepKey="createOption2"> + <argument name="adminName" value="{{multiselectProductAttribute.option2_admin}}"/> + <argument name="frontName" value="{{multiselectProductAttribute.option2_frontend}}"/> + <argument name="row" value="2"/> + </actionGroup> + <actionGroup ref="createAttributeDropdownNthOptionAsDefault" 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="navigateToEditProductAttribute" 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="addProductAttributeInProductModal" 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_frontend}}" stepKey="seeDefaultIsCorrect"/> + <see stepKey="seeOption1Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option1_frontend}}"/> + <see stepKey="seeOption2Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option2_frontend}}"/> + <see stepKey="seeOption3Available" selector="{{AdminProductAttributesSection.attributeDropdownByCode(multiselectProductAttribute.attribute_code)}}" userInput="{{multiselectProductAttribute.option3_frontend}}"/> + </test> </tests> From 83bdb2213c02f97d8c32f2a65a735272175ca34f Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Wed, 6 Feb 2019 15:27:40 -0600 Subject: [PATCH 054/254] MC-4410: Convert DeleteAssignedToTemplateProductAttributeTest to MFTF --- .../Test/Mftf/Data/ProductAttributeData.xml | 21 +++++ .../AdminProductAttributeSetGridSection.xml | 1 + .../Mftf/Section/AdminProductFormSection.xml | 1 + ...wnProductAttributeFromAttributeSetTest.xml | 72 +++++++++++++++ ...ldProductAttributeFromAttributeSetTest.xml | 88 +++++++++++++++++++ 5 files changed, 183 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index b367cdcab9d8b..89fb6bc033ee3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -115,4 +115,25 @@ <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> + <entity name="newProductAttribute" type="ProductAttribute"> + <data key="attribute_code" unique="suffix">attribute</data> + <data key="frontend_input">text</data> + <data key="scope">global</data> + <data key="is_required">false</data> + <data key="is_unique">false</data> + <data key="is_searchable">true</data> + <data key="is_visible">true</data> + <data key="is_visible_in_advanced_search">true</data> + <data key="is_visible_on_front">true</data> + <data key="is_filterable">true</data> + <data key="is_filterable_in_search">true</data> + <data key="used_in_product_listing">true</data> + <data key="is_used_for_promo_rules">true</data> + <data key="is_comparable">true</data> + <data key="is_used_in_grid">true</data> + <data key="is_visible_in_grid">true</data> + <data key="is_filterable_in_grid">true</data> + <data key="used_for_sort_by">true</data> + <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml index b906e2fa9084b..aaf8906466d74 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml @@ -14,5 +14,6 @@ <element name="nthRow" type="block" selector="#setGrid_table tbody tr:nth-of-type({{var1}})" parameterized="true"/> <element name="AttributeSetName" type="text" selector="//td[contains(text(), '{{var1}}')]" parameterized="true"/> <element name="addAttributeSetBtn" type="button" selector="button.add-set" timeout="30"/> + <element name="resetFilter" type="button" selector="//button[contains(.,'Reset Filter')]/span" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index e2ccbe165a0b2..0638d38a98296 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -57,6 +57,7 @@ <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"/> </section> <section name="ProductInWebsitesSection"> <element name="sectionHeader" type="button" selector="div[data-index='websites']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml new file mode 100644 index 0000000000000..3841c061c2629 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.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="AdminDeleteDropdownProductAttributeFromAttributeSetTest"> + <annotations> + <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"/> + <testCaseId value="MC-10885"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <!-- Create Dropdown Product Attribute --> + <createData entity="productDropDownAttribute" stepKey="attribute"/> + <!-- Create Attribute set --> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + </before> + <after> + <!--Delete Created Data --> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Open Product Attribute Set Page --> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> + <waitForPageLoad stepKey="waitForProductAttributeSetPageToLoad"/> + <click selector="{{AdminProductAttributeSetGridSection.resetFilter}}" stepKey="clickOnResetFilter"/> + <!-- Filter created Product Attribute Set --> + <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="$$createAttributeSet.attribute_set_name$$" stepKey="fillAttributeSetName"/> + <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickOnSearchButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName($$createAttributeSet.attribute_set_name$$)}}" stepKey="clickOnAttributeSet"/> + <waitForPageLoad stepKey="waitForAttributeSetEditPageToLoad"/> + <!--Assign Attribute to the Group and save the attribute set --> + <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttribute"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$attribute.attribute_code$$"/> + </actionGroup> + <click selector="{{AdminProductAttributeSetActionSection.save}}" stepKey="clickOnSaveButton"/> + <waitForPageLoad stepKey="waitForPageToSave"/> + <see userInput="You saved the attribute set" selector="{{AdminMessagesSection.success}}" stepKey="successMessage"/> + <!--Delete product attribute from product attribute grid --> + <actionGroup ref="deleteProductAttributeByAttributeCode" stepKey="deleteProductAttribute"> + <argument name="ProductAttributeCode" value="$$attribute.attribute_code$$"/> + </actionGroup> + <!--Confirm Attribute is not present in Product Attribute Grid --> + <actionGroup ref="filterProductAttributeByAttributeCode" stepKey="filterAttribute"> + <argument name="ProductAttributeCode" value="$$attribute.attribute_code$$"/> + </actionGroup> + <see selector="{{AdminProductAttributeGridSection.FirstRow}}" userInput="We couldn't find any records." stepKey="seeEmptyRow"/> + <!-- Verify Attribute is not present in Product Attribute Set Page --> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets1"/> + <waitForPageLoad stepKey="waitForProductAttributeSetPageToLoad1"/> + <click selector="{{AdminProductAttributeSetGridSection.resetFilter}}" stepKey="clickOnResetFilter1"/> + <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="$$createAttributeSet.attribute_set_name$$" stepKey="fillAttributeSetName1"/> + <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickOnSearchButton1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <click selector="{{AdminProductAttributeSetGridSection.AttributeSetName($$createAttributeSet.attribute_set_name$$)}}" stepKey="clickOnAttributeSet1"/> + <waitForPageLoad stepKey="waitForAttributeSetEditPageToLoad1"/> + <dontSee userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="dontSeeAttributeInAttributeGroupTree"/> + <dontSee userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="dontSeeAttributeInUnassignedAttributeTree"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml new file mode 100644 index 0000000000000..c3cafb17c5eac --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.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="AdminDeleteTextFieldProductAttributeFromAttributeSetTest"> + <annotations> + <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"/> + <testCaseId value="MC-10886"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <!-- Create Product Attribute and assign to Default Product Attribute Set --> + <createData entity="newProductAttribute" stepKey="attribute"/> + <createData entity="AddToDefaultSet" stepKey="addToDefaultAttributeSet"> + <requiredEntity createDataKey="attribute"/> + </createData> + <!-- Create Simple Product --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + </before> + <after> + <!--Delete cteated Data --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimplaeProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Open Product Attribute Set Page --> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> + <waitForPageLoad stepKey="waitForProductAttributeSetPageToLoad"/> + <click selector="{{AdminProductAttributeSetGridSection.resetFilter}}" stepKey="clickOnResetFilter"/> + <!--Select Default Product Attribute Set --> + <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="Default" stepKey="fillAttributeSetName"/> + <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickOnSearchButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="waitForAttributeSetEditPageToLoad"/> + <see selector="{{AdminProductAttributeSetEditSection.groupTree}}" userInput="$$attribute.attribute_code$$" stepKey="seeAttributeInAttributeGroupTree"/> + <!--Open Product Index Page and filter the product--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <argument name="product" value="SimpleProduct2"/> + </actionGroup> + <!--Verify Created Product Attribute displayed in Product page --> + <click stepKey="openSelectedProduct" selector="{{AdminProductGridSection.productRowBySku($$createSimpleProduct.sku$$)}}"/> + <waitForPageLoad stepKey="waitForProductToLoad"/> + <seeElement selector="{{AdminProductFormSection.newAddedAttribute($$attribute.attribute_code$$)}}" stepKey="seeProductAttributeIsAdded"/> + <!--Delete product attribute from product attribute grid --> + <actionGroup ref="deleteProductAttributeByAttributeCode" stepKey="deleteProductAttribute"> + <argument name="ProductAttributeCode" value="$$attribute.attribute_code$$"/> + </actionGroup> + <!-- Confirm attribute is not present in product attribute grid --> + <actionGroup ref="filterProductAttributeByAttributeCode" stepKey="filterAttribute"> + <argument name="ProductAttributeCode" value="$$attribute.attribute_code$$"/> + </actionGroup> + <see stepKey="seeEmptyRow" selector="{{AdminProductAttributeGridSection.FirstRow}}" userInput="We couldn't find any records."/> + <!-- Verify Attribute is not present in Product Attribute Set Page --> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets1"/> + <waitForPageLoad stepKey="waitForProductAttributeSetPageToLoad1"/> + <click selector="{{AdminProductAttributeSetGridSection.resetFilter}}" stepKey="clickOnResetFilter1"/> + <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="Default" stepKey="fillAttributeSetName1"/> + <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickOnSearchButton1"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow1"/> + <waitForPageLoad stepKey="waitForAttributeSetEditPageToLoad1"/> + <dontSee userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="dontSeeAttributeInAttributeGroupTree"/> + <dontSee userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="dontSeeAttributeInUnassignedAttributeTree"/> + <!--Verify Product Attribute is not present in Product Index Page --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductIndexPage"/> + <waitForPageLoad stepKey="waitForProductIndexPageToLoad1"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProduct1"> + <argument name="product" value="SimpleProduct2"/> + </actionGroup> + <!--Verify Product Attribute is not present in Product page --> + <click selector="{{AdminProductGridSection.productRowBySku($$createSimpleProduct.sku$$)}}" stepKey="openSelectedProduct1"/> + <waitForPageLoad stepKey="waitForProductPageToLoad"/> + <dontSeeElement selector="{{AdminProductFormSection.newAddedAttribute($$attribute.attribute_code$$)}}" stepKey="dontSeeProductAttribute"/> + </test> +</tests> From e95c678c4db2b29fad6bf7f319dcb1f16dd4c15f Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Tue, 29 Jan 2019 14:03:10 -0500 Subject: [PATCH 055/254] Proposal For setPaymentMethodOnCart Ref #293 --- app/code/Magento/BraintreeGraphQl/README.md | 4 + .../Magento/BraintreeGraphQl/composer.json | 24 ++ .../BraintreeGraphQl/etc/graphql/di.xml | 27 ++ .../Magento/BraintreeGraphQl/etc/module.xml | 10 + .../BraintreeGraphQl/etc/schema.graphqls | 18 + .../Magento/BraintreeGraphQl/registration.php | 10 + .../AdditionalDataBuilderInterface.php | 13 + .../Payment/AdditionalDataBuilderPool.php | 32 ++ .../Payment/DefaultAdditionalDataBuilder.php | 43 +++ .../Model/Cart/Payment/MethodBuilder.php | 62 ++++ .../Cart/Payment/PaymentDataProvider.php | 35 ++ .../Model/Resolver/CartPaymentMethod.php | 58 +++ .../Model/Resolver/SetPaymentMethodOnCart.php | 102 ++++++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 21 ++ composer.json | 1 + composer.lock | 4 +- .../Quote/SetPaymentMethodOnCartTest.php | 335 ++++++++++++++++++ 17 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/BraintreeGraphQl/README.md create mode 100644 app/code/Magento/BraintreeGraphQl/composer.json create mode 100644 app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml create mode 100644 app/code/Magento/BraintreeGraphQl/etc/module.xml create mode 100644 app/code/Magento/BraintreeGraphQl/etc/schema.graphqls create mode 100644 app/code/Magento/BraintreeGraphQl/registration.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderInterface.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderPool.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/DefaultAdditionalDataBuilder.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/MethodBuilder.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/PaymentDataProvider.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/CartPaymentMethod.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php diff --git a/app/code/Magento/BraintreeGraphQl/README.md b/app/code/Magento/BraintreeGraphQl/README.md new file mode 100644 index 0000000000000..f6740e4d250e9 --- /dev/null +++ b/app/code/Magento/BraintreeGraphQl/README.md @@ -0,0 +1,4 @@ +# BraintreeGraphQl + +**BraintreeGraphQl** provides type and resolver for method additional +information. \ No newline at end of file diff --git a/app/code/Magento/BraintreeGraphQl/composer.json b/app/code/Magento/BraintreeGraphQl/composer.json new file mode 100644 index 0000000000000..a322db9d257dc --- /dev/null +++ b/app/code/Magento/BraintreeGraphQl/composer.json @@ -0,0 +1,24 @@ +{ + "name": "magento/module-braintree-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*" + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\BraintreeGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml b/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml new file mode 100644 index 0000000000000..3788a0c2f1325 --- /dev/null +++ b/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml @@ -0,0 +1,27 @@ +<?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\QuoteGraphQl\Model\Cart\Payment\AdditionalDataBuilderPool"> + <arguments> + <argument name="builders" xsi:type="array"> + <item name="braintree" xsi:type="object">BraintreeAdditionalDataBuilder</item> + <item name="braintree_vault" xsi:type="object">BraintreeVaultAdditionalDataBuilder</item> + </argument> + </arguments> + </type> + <virtualType name="BraintreeAdditionalDataBuilder" type="Magento\QuoteGraphQl\Model\Cart\Payment\DefaultAdditionalDataBuilder"> + <arguments> + <argument name="methodCode" xsi:type="const">Magento\Braintree\Model\Ui\ConfigProvider::CODE</argument> + </arguments> + </virtualType> + <virtualType name="BraintreeVaultAdditionalDataBuilder" type="Magento\QuoteGraphQl\Model\Cart\Payment\DefaultAdditionalDataBuilder"> + <arguments> + <argument name="methodCode" xsi:type="const">Magento\Braintree\Model\Ui\ConfigProvider::CC_VAULT_CODE</argument> + </arguments> + </virtualType> +</config> diff --git a/app/code/Magento/BraintreeGraphQl/etc/module.xml b/app/code/Magento/BraintreeGraphQl/etc/module.xml new file mode 100644 index 0000000000000..2133e95a69104 --- /dev/null +++ b/app/code/Magento/BraintreeGraphQl/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_BraintreeGraphQl"/> +</config> diff --git a/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls b/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..83c98a68d85ee --- /dev/null +++ b/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls @@ -0,0 +1,18 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +input PaymentMethodInput { + braintree: BraintreeInput + braintree_vault: BraintreeVaultInput +} + +input BraintreeInput { + payment_method_nonce: String! + is_active_payment_token_enabler: Boolean! +} + +input BraintreeVaultInput { + payment_method_nonce: String! + public_hash: String! + is_active_payment_token_enabler: Boolean! +} diff --git a/app/code/Magento/BraintreeGraphQl/registration.php b/app/code/Magento/BraintreeGraphQl/registration.php new file mode 100644 index 0000000000000..37f7ef30864cb --- /dev/null +++ b/app/code/Magento/BraintreeGraphQl/registration.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_BraintreeGraphQl', __DIR__); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderInterface.php new file mode 100644 index 0000000000000..36a5724724a79 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderInterface.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart\Payment; + +interface AdditionalDataBuilderInterface +{ + public function build(array $args): array; +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderPool.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderPool.php new file mode 100644 index 0000000000000..dde47507553b5 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderPool.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart\Payment; + +class AdditionalDataBuilderPool +{ + /** + * @var AdditionalDataBuilderInterface[] + */ + private $builders = []; + + public function __construct( + array $builders + ) { + $this->builders = $builders; + } + + public function buildForMethod(string $methodCode, array $args): array + { + $additionalData = []; + if (isset($this->builders[$methodCode])) { + $additionalData = $this->builders[$methodCode]->build($args); + } + + return $additionalData; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/DefaultAdditionalDataBuilder.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/DefaultAdditionalDataBuilder.php new file mode 100644 index 0000000000000..5a032ccc6386d --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/DefaultAdditionalDataBuilder.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart\Payment; + +use Magento\Framework\Stdlib\ArrayManager; + +class DefaultAdditionalDataBuilder implements AdditionalDataBuilderInterface +{ + private const INPUT_PATH_ADDITIONAL_DATA = 'input/payment_method/%s'; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @var string + */ + private $methodCode; + + public function __construct( + ArrayManager $arrayManager, + string $methodCode = '' + ) { + $this->arrayManager = $arrayManager; + $this->methodCode = $methodCode; + } + + public function build(array $args): array + { + return $this->arrayManager->get($this->getAdditionalDataPath(), $args) ?? []; + } + + private function getAdditionalDataPath(): string + { + return sprintf(static::INPUT_PATH_ADDITIONAL_DATA, $this->methodCode); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/MethodBuilder.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/MethodBuilder.php new file mode 100644 index 0000000000000..110e24d7d0fe5 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/MethodBuilder.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart\Payment; + +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Stdlib\ArrayManager; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\Quote\Api\Data\PaymentInterfaceFactory; + +class MethodBuilder +{ + /** + * @var PaymentInterfaceFactory + */ + private $paymentFactory; + + /** + * @var AdditionalDataBuilderPool + */ + private $additionalDataBuilderPool; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @param PaymentInterfaceFactory $paymentFactory + * @param AdditionalDataBuilderPool $additionalDataBuilderPool + * @param ArrayManager $arrayManager + */ + public function __construct( + PaymentInterfaceFactory $paymentFactory, + AdditionalDataBuilderPool $additionalDataBuilderPool, + ArrayManager $arrayManager + ) { + $this->paymentFactory = $paymentFactory; + $this->additionalDataBuilderPool = $additionalDataBuilderPool; + $this->arrayManager = $arrayManager; + } + + public function build(array $args): PaymentInterface + { + $method = (string) $this->arrayManager->get('input/payment_method/method', $args); + + return $this->paymentFactory->create([ + 'data' => [ + PaymentInterface::KEY_METHOD => $method, + PaymentInterface::KEY_PO_NUMBER => $this->arrayManager->get('input/payment_method/po_number', $args), + PaymentInterface::KEY_ADDITIONAL_DATA => $this->additionalDataBuilderPool->buildForMethod( + $method, + $args + ), + ] + ]); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/PaymentDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/PaymentDataProvider.php new file mode 100644 index 0000000000000..22adc69d1cc58 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/PaymentDataProvider.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart\Payment; + +use Magento\Quote\Model\Quote; + +/** + * Extract data from payment + */ +class PaymentDataProvider +{ + /** + * Extract data from cart + * + * @param Quote $cart + * @return array + */ + public function getCartPayment(Quote $cart): array + { + $payment = $cart->getPayment(); + if (!$payment) { + return []; + } + + return [ + 'method' => $payment->getMethod(), + 'po_number' => $payment->getPoNumber(), + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartPaymentMethod.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartPaymentMethod.php new file mode 100644 index 0000000000000..9e75256cd9b2b --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartPaymentMethod.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\QuoteGraphQl\Model\Cart\Payment\PaymentDataProvider; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; + +/** + * @inheritdoc + */ +class CartPaymentMethod implements ResolverInterface +{ + /** + * @var PaymentDataProvider + */ + private $paymentDataProvider; + + /** + * @var GetCartForUser + */ + private $getCartForUser; + + /** + * @param PaymentDataProvider $paymentDataProvider + * @param GetCartForUser $getCartForUser + */ + public function __construct( + PaymentDataProvider $paymentDataProvider, + GetCartForUser $getCartForUser + ) { + $this->paymentDataProvider = $paymentDataProvider; + $this->getCartForUser = $getCartForUser; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['cart_id'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + + $maskedCartId = $value['cart_id']; + $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); + + return $this->paymentDataProvider->getCartPayment($cart); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php new file mode 100644 index 0000000000000..5e48b3413f7ce --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +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\Framework\Stdlib\ArrayManager; +use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\Quote\Api\PaymentMethodManagementInterface; +use Magento\QuoteGraphQl\Model\Cart\Payment\MethodBuilder; + +/** + * Mutation resolver for setting payment method for shopping cart + */ +class SetPaymentMethodOnCart implements ResolverInterface +{ + /** + * @var MaskedQuoteIdToQuoteIdInterface + */ + private $maskedQuoteIdToQuoteId; + + /** + * @var GetCartForUser + */ + private $getCartForUser; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @var PaymentMethodManagementInterface + */ + private $paymentMethodManagement; + + /** + * @var MethodBuilder + */ + private $methodBuilder; + + /** + * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId + * @param GetCartForUser $getCartForUser + * @param ArrayManager $arrayManager + * @param PaymentMethodManagementInterface $paymentMethodManagement + * @param MethodBuilder $methodBuilder + */ + public function __construct( + MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, + GetCartForUser $getCartForUser, + ArrayManager $arrayManager, + PaymentMethodManagementInterface $paymentMethodManagement, + MethodBuilder $methodBuilder + ) { + $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; + $this->getCartForUser = $getCartForUser; + $this->arrayManager = $arrayManager; + $this->paymentMethodManagement = $paymentMethodManagement; + $this->methodBuilder = $methodBuilder; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + $paymentMethod = $this->arrayManager->get('input/payment_method', $args); + $maskedCartId = $this->arrayManager->get('input/cart_id', $args); + + if (!$maskedCartId) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + } + if (!$paymentMethod) { + throw new GraphQlInputException(__('Required parameter "payment_method" is missing')); + } + + $maskedCartId = $args['input']['cart_id']; + $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); + + try { + $this->paymentMethodManagement->set($cart->getId(), $this->methodBuilder->build($args)); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } + + return [ + 'cart' => [ + 'cart_id' => $maskedCartId, + ], + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 4c1101a5f90a8..e1dd06f64cf88 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -15,6 +15,17 @@ type Mutation { setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart") addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") + setPaymentMethodOnCart(input: SetPaymentMethodOnCartInput): SetPaymentMethodOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentMethodOnCart") +} + +input SetPaymentMethodOnCartInput { + cart_id: String! + payment_method: PaymentMethodInput! +} + +input PaymentMethodInput { + method: String! + po_number: String } input SetShippingAddressesOnCartInput { @@ -64,6 +75,10 @@ input ShippingMethodForAddressInput { shipping_method_code: String! } +type SetPaymentMethodOnCartOutput { + cart: Cart! +} + type SetBillingAddressOnCartOutput { cart: Cart! } @@ -98,6 +113,7 @@ type Cart { items: [CartItemInterface] applied_coupon: AppliedCoupon addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddresses") + payment_method: CartPaymentMethod @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartPaymentMethod") } type CartAddress { @@ -118,6 +134,11 @@ type CartAddress { cart_items: [CartItemQuantity] } +type CartPaymentMethod { + method: String + po_number: String +} + type CartItemQuantity { cart_item_id: String! quantity: Float! diff --git a/composer.json b/composer.json index 3222de2d1fe0f..69924d836b9e7 100644 --- a/composer.json +++ b/composer.json @@ -108,6 +108,7 @@ "magento/module-backend": "*", "magento/module-backup": "*", "magento/module-braintree": "*", + "magento/module-braintree-graph-ql": "*", "magento/module-bundle": "*", "magento/module-bundle-graph-ql": "*", "magento/module-bundle-import-export": "*", diff --git a/composer.lock b/composer.lock index 697e7df3e19aa..172866d59f4ff 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "3f58ddc5609e6a934ee3706006357646", + "content-hash": "bee21a1a1fa89a8a2dc5aedcd087d1e7", "packages": [ { "name": "braintree/braintree_php", diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php new file mode 100644 index 0000000000000..cd16123d428b0 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php @@ -0,0 +1,335 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\OfflinePayments\Model\Banktransfer; +use Magento\OfflinePayments\Model\Cashondelivery; +use Magento\OfflinePayments\Model\Checkmo; +use Magento\OfflinePayments\Model\Purchaseorder; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Config\Model\ResourceModel\Config; + +/** + * Test for setting payment methods on cart + */ +class SetPaymentMethodOnCartTest extends GraphQlAbstract +{ + private const OFFLINE_METHOD_CODES = [ + Checkmo::PAYMENT_METHOD_CHECKMO_CODE, + Banktransfer::PAYMENT_METHOD_BANKTRANSFER_CODE, + Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE, + Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE, + ]; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var Quote + */ + private $quote; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @var Config + */ + private $config; + + /** + * @var TypeListInterface + */ + private $cacheList; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->create(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->config = $objectManager->get(Config::class); + $this->cacheList = $objectManager->get(TypeListInterface::class); + + foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { + $this->config->saveConfig( + 'payment/' . $offlineMethodCode . '/active', + '1', + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + } + $this->cacheList->cleanType('config'); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { + //Never no disable checkmo method + if ($offlineMethodCode === Checkmo::PAYMENT_METHOD_CHECKMO_CODE) { + continue; + } + $this->config->saveConfig( + 'payment/' . $offlineMethodCode . '/active', + '0', + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + } + $this->cacheList->cleanType('config'); + } + + /** + * @param string $methodCode + * @dataProvider dataProviderOfflinePaymentMethods + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetPaymentMethodOnCart(string $methodCode) + { + /** @var \Magento\Config\Model\ResourceModel\Config $config */ + $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); + $config->saveConfig( + 'payment/' . $methodCode . '/active', + 1, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $methodCode + ); + + $response = $this->sendRequestWithToken($query); + + $this->assertArrayHasKey('setPaymentMethodOnCart', $response); + $this->assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); + $this->assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); + $this->assertArrayHasKey('payment_method', $response['setPaymentMethodOnCart']['cart']); + $this->assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['payment_method']['method']); + } + + public function dataProviderOfflinePaymentMethods(): array + { + $methods = []; + foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { + //Purchase order requires additional input and is tested separately + if ($offlineMethodCode === Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE) { + continue; + } + $methods[] = [$offlineMethodCode]; + } + + return $methods; + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetNonExistingPaymentMethod() + { + $paymentMethod = 'noway'; + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $paymentMethod + ); + + $this->expectExceptionMessage('The requested Payment Method is not available.'); + $this->sendRequestWithToken($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetPaymentMethodByGuestToCustomerCart() + { + $paymentMethod = 'checkmo'; + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $paymentMethod + ); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetPaymentMethodPurchaseOrderOnCart() + { + $methodCode = \Magento\OfflinePayments\Model\Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE; + $poNumber = 'GQL-19002'; + + /** @var \Magento\Config\Model\ResourceModel\Config $config */ + $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); + $config->saveConfig( + 'payment/' . $methodCode . '/active', + 1, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<<QUERY +mutation { + setPaymentMethodOnCart(input: + { + cart_id: "$maskedQuoteId", + payment_method: { + method: "$methodCode" + po_number: "$poNumber" + } + }) { + + cart { + cart_id, + payment_method { + method + po_number + } + } + } +} + +QUERY; + + $response = $this->sendRequestWithToken($query); + + $this->assertArrayHasKey('setPaymentMethodOnCart', $response); + $this->assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); + $this->assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); + $this->assertArrayHasKey('payment_method', $response['setPaymentMethodOnCart']['cart']); + $this->assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['payment_method']['method']); + $this->assertEquals($poNumber, $response['setPaymentMethodOnCart']['cart']['payment_method']['po_number']); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testPurchaseOrderPaymentMethodFailingValidation() + { + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE + ); + + $this->expectExceptionMessage('Purchase order number is a required field.'); + $this->sendRequestWithToken($query); + } + + /** + * Generates query for setting the specified shipping method on cart + * + * @param string $maskedQuoteId + * @param string $methodCode + * @return string + */ + private function prepareMutationQuery( + string $maskedQuoteId, + string $methodCode + ) : string { + return <<<QUERY +mutation { + setPaymentMethodOnCart(input: + { + cart_id: "$maskedQuoteId", + payment_method: { + method: "$methodCode" + } + }) { + + cart { + cart_id, + payment_method { + method + } + } + } +} + +QUERY; + } + + /** + * Sends a GraphQL request with using a bearer token + * + * @param string $query + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function sendRequestWithToken(string $query): array + { + + $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + + return $this->graphQlQuery($query, [], '', $headerMap); + } +} From 8ec9a3ab8bfe02aa7bb4da56249dfe493c7fb049 Mon Sep 17 00:00:00 2001 From: jaideepghosh <er.jaideepghosh@gmail.com> Date: Thu, 7 Feb 2019 19:44:41 +0530 Subject: [PATCH 056/254] Update static block in nginx.conf Update the 'nginx.conf.sample' to allow the browsers for accessing the static contents even in multisite mode also. --- nginx.conf.sample | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nginx.conf.sample b/nginx.conf.sample index 90604808f6ec0..eec85c74cec69 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -99,16 +99,16 @@ location /static/ { # Remove signature of the static files that is used to overcome the browser cache location ~ ^/static/version { - rewrite ^/static/(version[^/]+/)?(.*)$ /static/$2 last; + rewrite ^/static/(version\d*/)?(.*)$ /static/$2 last; } - location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2|json)$ { + location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; if (!-f $request_filename) { - rewrite ^/static/?(.*)$ /static.php?resource=$1 last; + rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } location ~* \.(zip|gz|gzip|bz2|csv|xml)$ { @@ -117,11 +117,11 @@ location /static/ { expires off; if (!-f $request_filename) { - rewrite ^/static/?(.*)$ /static.php?resource=$1 last; + rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } } if (!-f $request_filename) { - rewrite ^/static/?(.*)$ /static.php?resource=$1 last; + rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last; } add_header X-Frame-Options "SAMEORIGIN"; } From e5603018be8113f3acd124b896da4666c538dcea Mon Sep 17 00:00:00 2001 From: jaideepghosh <er.jaideepghosh@gmail.com> Date: Thu, 7 Feb 2019 19:49:47 +0530 Subject: [PATCH 057/254] update nginx.conf.sample to serve json also. --- nginx.conf.sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.conf.sample b/nginx.conf.sample index eec85c74cec69..ce3891627bc8c 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -102,7 +102,7 @@ location /static/ { rewrite ^/static/(version\d*/)?(.*)$ /static/$2 last; } - location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2)$ { + location ~* \.(ico|jpg|jpeg|png|gif|svg|js|css|swf|eot|ttf|otf|woff|woff2|json)$ { add_header Cache-Control "public"; add_header X-Frame-Options "SAMEORIGIN"; expires +1y; From dac75945cc1969f4efdfb0a05e516cb5fa037b3c Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Thu, 7 Feb 2019 12:17:08 -0600 Subject: [PATCH 058/254] MC-4408: Convert UpdateSimpleProductEntityTest to MFTF --- .../Catalog/Test/Mftf/Data/ProductData.xml | 125 +++++++++++++- .../Mftf/Data/SimpleProductOptionData.xml | 20 +++ .../Catalog/Test/Mftf/Data/TierPriceData.xml | 8 +- ...minProductFormAdvancedInventorySection.xml | 7 +- .../Mftf/Section/AdminProductFormSection.xml | 3 +- .../Section/StorefrontProductPageSection.xml | 3 +- ...rifyDataOverridingOnStoreViewLevelTest.xml | 86 ++++++++++ ...rifyDataOverridingOnStoreViewLevelTest.xml | 84 ++++++++++ ...dminUpdateSimpleProductTieredPriceTest.xml | 142 ++++++++++++++++ ...RegularPriceInStockDisabledProductTest.xml | 93 +++++++++++ ...WithRegularPriceInStockEnabledFlatTest.xml | 141 ++++++++++++++++ ...PriceInStockNotVisibleIndividuallyTest.xml | 104 ++++++++++++ ...arPriceInStockUnassignFromCategoryTest.xml | 68 ++++++++ ...ceInStockVisibleInCatalogAndSearchTest.xml | 126 ++++++++++++++ ...arPriceInStockVisibleInCatalogOnlyTest.xml | 126 ++++++++++++++ ...larPriceInStockVisibleInSearchOnlyTest.xml | 126 ++++++++++++++ ...gularPriceInStockWithCustomOptionsTest.xml | 156 ++++++++++++++++++ ...eProductWithRegularPriceOutOfStockTest.xml | 124 ++++++++++++++ .../StorefrontQuickSearchResultsSection.xml | 2 +- .../CreateCustomStoreViewActionGroup.xml | 17 ++ 20 files changed, 1551 insertions(+), 10 deletions(-) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Data/SimpleProductOptionData.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 47dff6cb17c85..6cc5117069062 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -696,4 +696,127 @@ <data key="urlKey" unique="suffix">virtual-product</data> <data key="type_id">virtual</data> </entity> -</entities> + <entity name="simpleProductRegularPrice325InStock" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">325.02</data> + <data key="quantity">89</data> + <data key="status">In Stock</data> + <data key="storefrontStatus">IN STOCK</data> + <data key="weight">89.0000</data> + <data key="visibility">Search</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> + <entity name="simpleProductRegularPrice32503OutOfStock" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">325.03</data> + <data key="quantity">25</data> + <data key="status">Out of Stock</data> + <data key="storefrontStatus">OUT OF STOCK</data> + <data key="weight">125.0000</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> + <entity name="simpleProductRegularPrice245InStock" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">245.00</data> + <data key="quantity">200</data> + <data key="status">In Stock</data> + <data key="storefrontStatus">IN STOCK</data> + <data key="weight">120.0000</data> + <data key="visibility">Catalog, Search</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> + <entity name="simpleProductRegularPrice32501InStock" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">325.01</data> + <data key="quantity">125</data> + <data key="status">In Stock</data> + <data key="storefrontStatus">IN STOCK</data> + <data key="weight">25.0000</data> + <data key="visibility">Catalog</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> + <entity name="simpleProductTierPrice300InStock" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">300.00</data> + <data key="quantity">34</data> + <data key="status">In Stock</data> + <data key="storefrontStatus">IN STOCK</data> + <data key="weight">1</data> + <data key="weightSelect">This item has weight</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> + <entity name="simpleProductEnabledFlat" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">1.99</data> + <data key="productTaxClass">Taxable Goods</data> + <data key="quantity">1000</data> + <data key="status">1</data> + <data key="storefrontStatus">IN STOCK</data> + <data key="weight">1</data> + <data key="weightSelect">This item has weight</data> + <data key="visibility">Catalog, Search</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> + <entity name="simpleProductRegularPriceCustomOptions" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">245.00</data> + <data key="storefront_new_cartprice">343.00</data> + <data key="quantity">200</data> + <data key="status">In Stock</data> + <data key="storefrontStatus">IN STOCK</data> + <data key="weight">120.0000</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> + <entity name="simpleProductDisabled" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">74.00</data> + <data key="quantity">87</data> + <data key="status">In Stock</data> + <data key="weight">333.0000</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> + <entity name="simpleProductNotVisibleIndividually" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">325.00</data> + <data key="quantity">123</data> + <data key="status">In Stock</data> + <data key="weight">129.0000</data> + <data key="visibility">Not Visible Individually</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> + <entity name="simpleProductDataOverriding" type="product"> + <data key="urlKey" unique="suffix">test-simple-product</data> + <data key="name" unique="suffix">TestSimpleProduct</data> + <data key="sku" unique="suffix">test_simple_product_sku</data> + <data key="price">9.99</data> + <data key="type_id">simple</data> + <requiredEntity type="product_extension_attribute">EavStock100</requiredEntity> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/SimpleProductOptionData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/SimpleProductOptionData.xml new file mode 100644 index 0000000000000..157a4d410263b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/SimpleProductOptionData.xml @@ -0,0 +1,20 @@ +<?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="simpleProductCustomizableOption"> + <data key="title" unique="suffix">Test3 option</data> + <data key="type">Drop-down</data> + <data key="is_required">1</data> + <data key="option_0_title" unique="suffix">40 Percent</data> + <data key="option_0_price">40.00</data> + <data key="option_0_price_type">Percent</data> + <data key="option_0_sku" unique="suffix">sku_drop_down_row_1</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/TierPriceData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/TierPriceData.xml index 7fd0c65a2f753..c3207f540af5c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/TierPriceData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/TierPriceData.xml @@ -42,4 +42,10 @@ <data key="price_1">24.00</data> <data key="qty_1">15</data> </entity> -</entities> + <entity name="tierPriceHighCostSimpleProduct" type="data"> + <data key="website">All Websites [USD]</data> + <data key="customer_group">ALL GROUPS</data> + <data key="price">500,000.00</data> + <data key="qty">1</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml index a1bb27bb45a29..e027c54e5c17e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedInventorySection.xml @@ -19,9 +19,6 @@ <element name="doneButton" type="button" selector="//aside[contains(@class,'product_form_product_form_advanced_inventory_modal')]//button[contains(@data-role,'action')]" timeout="5"/> <element name="useConfigSettings" type="checkbox" selector="//input[@name='product[stock_data][use_config_manage_stock]']"/> <element name="manageStock" type="select" selector="//*[@name='product[stock_data][manage_stock]']"/> + <element name="advancedInventoryCloseButton" type="button" selector=".product_form_product_form_advanced_inventory_modal button.action-close" timeout="30"/> </section> -</sections> - - - - +</sections> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml index ad3e140c0818e..e06f953434b43 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml @@ -27,6 +27,7 @@ <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="categoriesDropdown" type="multiselect" selector="div[data-index='category_ids']"/> + <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[contains(@data-index, 'advanced_inventory_button')]" timeout="30"/> <element name="productStockStatus" type="select" selector="select[name='product[quantity_and_stock_status][is_in_stock]']"/> @@ -183,4 +184,4 @@ <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"/> </section> -</sections> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml index e9c8f53f97e5f..8055ecfe00cde 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductPageSection.xml @@ -22,5 +22,6 @@ <element name="subTotal" type="input" selector="span[data-th='Subtotal']"/> <element name="shipping" type="input" selector="span[data-th='Shipping']"/> <element name="orderTotal" type="input" selector=".grand.totals .amount .price"/> + <element name="customOptionDropDown" type="select" selector="//*[@id='product-options-wrapper']//select[contains(@class, 'product-custom-option admin__control-select')]"/> </section> -</sections> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml new file mode 100644 index 0000000000000..a5c2e1aa3a6de --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.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="AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest"> + <annotations> + <stories value="Update Simple Product"/> + <title value="Update Simple Product Name to Verify Data Overriding on Store View Level"/> + <description value="Test log in to Update Simple Product and Update Simple Product Name to Verify Data Overriding on Store View Level"/> + <testCaseId value="MC-10821"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <argument name="storeView" value="customStoreFR"/> + </actionGroup> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{defaultSimpleProduct.sku}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> + <argument name="customStore" value="customStoreFR"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in grid --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Assign simple product to created store view --> + <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="clickCategoryStoreViewDropdownToggle"/> + <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewOption(customStoreFR.name)}}" stepKey="selectCategoryStoreViewOption"/> + <click selector="{{AdminProductFormChangeStoreSection.acceptButton}}" stepKey="clickAcceptButton"/> + <waitForPageLoad stepKey="waitForThePageToLoad"/> + <uncheckOption selector="{{AdminProductFormSection.productNameUseDefault}}" stepKey="uncheckProductStatus"/> + <!-- Update default simple product with name --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductDataOverriding.name}}" stepKey="fillSimpleProductName"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!--Verify customer see default simple product name on magento storefront page --> + <amOnPage url="{{StorefrontProductPage.url($$initialSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="$$initialSimpleProduct.sku$$" stepKey="fillDefaultSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="$$initialSimpleProduct.name$$" stepKey="seeDefaultProductName"/> + + <!--Verify customer see simple product with updated name on magento storefront page under store view section --> + <click selector="{{StorefrontHeaderSection.storeViewSwitcher}}" stepKey="clickStoreViewSwitcher"/> + <waitForPageLoad stepKey="waitForStoreSwitcherLoad"/> + <click selector="{{StorefrontHeaderSection.storeView(customStoreFR.name)}}" stepKey="clickStoreViewOption"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="$$initialSimpleProduct.sku$$" stepKey="fillDefaultSimpleProductSkuInSearch"/> + <waitForPageLoad stepKey="waitForSearchTextBoxLoad"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextButton"/> + <waitForPageLoad stepKey="waitForTextSearchLoad"/> + <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{simpleProductDataOverriding.name}}" stepKey="seeUpdatedSimpleProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml new file mode 100644 index 0000000000000..cd18110368919 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.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="AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest"> + <annotations> + <stories value="Update Simple Product"/> + <title value="Update Simple Product Price to Verify Data Overriding on Store View Level"/> + <description value="Test log in to Update Simple Product and Update Simple Product Price to Verify Data Overriding on Store View Level"/> + <testCaseId value="MC-10823"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> + <argument name="storeView" value="customStoreFR"/> + </actionGroup> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{defaultSimpleProduct.sku}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> + <argument name="customStore" value="customStoreFR"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in grid --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + <!-- Assign simple product to created store view --> + <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="clickCategoryStoreViewDropdownToggle"/> + <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewOption(customStoreFR.name)}}" stepKey="selectCategoryStoreViewOption"/> + <click selector="{{AdminProductFormChangeStoreSection.acceptButton}}" stepKey="clickAcceptButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <!-- Update default simple product with price --> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductDataOverriding.price}}" stepKey="fillSimpleProductPrice"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!-- Verify customer see simple product with updated price on magento storefront page --> + <amOnPage url="{{StorefrontProductPage.url($$initialSimpleProduct.custom_attributes[url_key]$$)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="$$initialSimpleProduct.sku$$" stepKey="fillDefaultSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <see selector="{{StorefrontQuickSearchResultsSection.regularPrice}}" userInput="{{simpleProductDataOverriding.price}}" stepKey="seeUpdatedProductPriceOnStorefrontPage"/> + + <!-- Verify customer see simple product with updated price on magento storefront page under store view section --> + <click selector="{{StorefrontHeaderSection.storeViewSwitcher}}" stepKey="clickStoreViewSwitcher"/> + <waitForPageLoad stepKey="waitForStoreSwitcherLoad"/> + <click selector="{{StorefrontHeaderSection.storeView(customStoreFR.name)}}" stepKey="clickStoreViewOption"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="$$initialSimpleProduct.sku$$" stepKey="fillDefaultSimpleProductSkuInSearch"/> + <waitForPageLoad stepKey="waitForSearchTextBoxLoad"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextButton"/> + <waitForPageLoad stepKey="waitForTextSearchLoad"/> + <see selector="{{StorefrontQuickSearchResultsSection.regularPrice}}" userInput="{{simpleProductDataOverriding.price}}" stepKey="seeUpdatedProductPriceOnStorefrontPageUnderStoreViewSection"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml new file mode 100644 index 0000000000000..e63b1fdfe31bc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.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="AdminUpdateSimpleProductTieredPriceTest"> + <annotations> + <stories value="Update Simple Product"/> + <title value="Update Simple Product Tiered Price"/> + <description value="Test log in to Update Simple Product and Update Simple Product Tiered Price"/> + <testCaseId value="MC-10824"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{simpleProductTierPrice300InStock.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in the grid --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product with tier price(in stock) --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductTierPrice300InStock.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductTierPrice300InStock.sku}}" stepKey="fillSimpleProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductTierPrice300InStock.price}}" stepKey="fillSimpleProductPrice"/> + <!-- 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="{{tierPriceHighCostSimpleProduct.website}}" stepKey="selectProductTierPriceWebsiteInput"/> + <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceHighCostSimpleProduct.customer_group}}" stepKey="selectProductTierPriceCustomerGroupInput"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceHighCostSimpleProduct.qty}}" stepKey="fillProductTierPriceQuantityInput"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceHighCostSimpleProduct.price}}" stepKey="selectProductTierPriceFixedPrice"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductTierPrice300InStock.quantity}}" stepKey="fillSimpleProductQuantity"/> + <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{simpleProductTierPrice300InStock.status}}" stepKey="selectStockStatusInStock"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductTierPrice300InStock.weight}}" stepKey="fillSimpleProductWeight"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="{{simpleProductTierPrice300InStock.weightSelect}}" stepKey="selectProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory" /> + <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory" /> + <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductTierPrice300InStock.urlKey}}" stepKey="fillUrlKey"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!-- Search updated simple product(from above step) in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> + <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="{{simpleProductTierPrice300InStock.name}}" stepKey="fillSimpleProductNameInNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{simpleProductTierPrice300InStock.sku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedSimpleProductVisibleInGrid"/> + <waitForPageLoad stepKey="waitUntilSimpleProductPageIsOpened"/> + + <!-- Verify customer see updated simple product in the product form page --> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductTierPrice300InStock.name}}" stepKey="seeSimpleProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductTierPrice300InStock.sku}}" stepKey="seeSimpleProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductTierPrice300InStock.price}}" stepKey="seeSimpleProductPrice"/> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink1"/> + <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{tierPriceHighCostSimpleProduct.website}}" stepKey="seeProductTierPriceWebsiteInput"/> + <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceHighCostSimpleProduct.customer_group}}" stepKey="seeProductTierPriceCustomerGroupInput"/> + <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceHighCostSimpleProduct.qty}}" stepKey="seeProductTierPriceQuantityInput"/> + <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceHighCostSimpleProduct.price}}" stepKey="seeProductTierPriceFixedPrice"/> + <click selector="{{AdminProductFormAdvancedPricingSection.advancedPricingCloseButton}}" stepKey="clickAdvancedPricingCloseButton"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductTierPrice300InStock.quantity}}" stepKey="seeSimpleProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{simpleProductTierPrice300InStock.status}}" stepKey="seeSimpleProductStockStatus"/> + <seeInField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductTierPrice300InStock.weight}}" stepKey="seeSimpleProductWeight"/> + <seeInField selector="{{AdminProductFormSection.productWeightSelect}}" userInput="{{simpleProductTierPrice300InStock.weightSelect}}" stepKey="seeSimpleProductWeightSelect"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> + <see selector="{{AdminProductFormSection.selectMultipleCategories}}" userInput="$$categoryEntity.name$$" stepKey="seeSelectedCategories"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductTierPrice300InStock.urlKey}}" stepKey="seeUrlKey"/> + + <!--Verify customer see updated simple product link on category page --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{simpleProductTierPrice300InStock.name}}" stepKey="seeSimpleProductNameOnCategoryPage"/> + + <!-- Verify customer see updated simple product (from the above step) on the storefront page --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductTierPrice300InStock.urlKey)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{simpleProductTierPrice300InStock.name}}" stepKey="seeSimpleProductNameOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{simpleProductTierPrice300InStock.price}}" stepKey="seeSimpleProductPriceOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{simpleProductTierPrice300InStock.sku}}" stepKey="seeProductSku"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="productStockAvailableStatus"/> + <assertEquals stepKey="assertStockAvailableOnProductPage"> + <expectedResult type="string">{{simpleProductTierPrice300InStock.storefrontStatus}}</expectedResult> + <actualResult type="variable">productStockAvailableStatus</actualResult> + </assertEquals> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="productPriceAmount"/> + <assertEquals stepKey="assertOldPriceTextOnProductPage"> + <expectedResult type="string">${{simpleProductTierPrice300InStock.price}}</expectedResult> + <actualResult type="variable">productPriceAmount</actualResult> + </assertEquals> + + <!--Verify customer see updated simple product link on magento storefront page and is searchable by sku --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductTierPrice300InStock.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{simpleProductTierPrice300InStock.sku}}" stepKey="fillSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{simpleProductTierPrice300InStock.name}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml new file mode 100644 index 0000000000000..7c2f8b4c458cc --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.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="AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest"> + <annotations> + <stories value="Update Simple Product"/> + <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"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{simpleProductDisabled.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product with regular price(in stock) --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductDisabled.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductDisabled.sku}}" stepKey="fillSimpleProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductDisabled.price}}" stepKey="fillSimpleProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductDisabled.quantity}}" stepKey="fillSimpleProductQuantity"/> + <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{simpleProductDisabled.status}}" stepKey="selectStockStatusInStock"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductDisabled.weight}}" stepKey="fillSimpleProductWeight"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductDisabled.urlKey}}" stepKey="fillUrlKey"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="clickEnableProductLabelToDisableProduct"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!-- Search updated simple product(from above step) in the grid --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> + <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="{{simpleProductDisabled.name}}" stepKey="fillSimpleProductNameInNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{simpleProductDisabled.sku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedSimpleProductVisibleInGrid"/> + <waitForPageLoad stepKey="waitUntilSimpleProductPageIsOpened"/> + + <!-- Verify customer see updated simple product in the product form page --> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductDisabled.name}}" stepKey="seeSimpleProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductDisabled.sku}}" stepKey="seeSimpleProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductDisabled.price}}" stepKey="seeSimpleProductPrice"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductDisabled.quantity}}" stepKey="seeSimpleProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{simpleProductDisabled.status}}" stepKey="seeSimpleProductStockStatus"/> + <seeInField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductDisabled.weight}}" stepKey="seeSimpleProductWeight"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSectionHeader"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> + <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductDisabled.urlKey}}" stepKey="seeSimpleProductUrlKey"/> + + <!--Verify customer don't see updated simple product link on magento storefront page --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductDisabled.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{simpleProductDisabled.sku}}" stepKey="fillSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <dontSee selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{simpleProductDisabled.name}}" stepKey="dontSeeProductNameOnStorefrontPage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml new file mode 100644 index 0000000000000..8f032b93c37b5 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.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="AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest"> + <annotations> + <stories value="Update Simple Product"/> + <title value="Update Simple Product with Regular Price (In Stock) Enabled Flat"/> + <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) Enabled Flat"/> + <testCaseId value="MC-10818"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <magentoCLI stepKey="setFlatCatalogProduct" command="config:set catalog/frontend/flat_catalog_product 1"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{simpleProductEnabledFlat.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI stepKey="unsetFlatCatalogProduct" command="config:set catalog/frontend/flat_catalog_product 0"/> + </after> + + <!-- Search default simple product in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product with regular price --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductEnabledFlat.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductEnabledFlat.sku}}" stepKey="fillSimpleProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductEnabledFlat.price}}" stepKey="fillSimpleProductPrice"/> + <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{simpleProductEnabledFlat.productTaxClass}}" stepKey="selectProductTaxClass"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductEnabledFlat.quantity}}" stepKey="fillSimpleProductQuantity"/> + <click selector="{{AdminProductFormSection.advancedInventoryLink}}" stepKey="clickAdvancedInventoryLink"/> + <waitForPageLoad stepKey="waitForAdvancedInventoryPage"/> + <conditionalClick selector="{{AdminProductFormAdvancedInventorySection.useConfigSettings}}" dependentSelector="{{AdminProductFormAdvancedInventorySection.useConfigSettings}}" visible="true" stepKey="checkUseConfigSettingsCheckBox"/> + <selectOption selector="{{AdminProductFormAdvancedInventorySection.manageStock}}" userInput="No" stepKey="selectManageStock"/> + <click selector="{{AdminProductFormAdvancedInventorySection.doneButton}}" stepKey="clickDoneButtonOnAdvancedInventorySection"/> + <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{simpleProductEnabledFlat.status}}" stepKey="selectStockStatusInStock"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductEnabledFlat.weight}}" stepKey="fillSimpleProductWeight"/> + <selectOption selector="{{AdminProductFormSection.productWeightSelect}}" userInput="{{simpleProductEnabledFlat.weightSelect}}" stepKey="selectProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory" /> + <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory" /> + <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductEnabledFlat.visibility}}" stepKey="selectVisibility"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductEnabledFlat.urlKey}}" stepKey="fillUrlKey"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!-- Search updated simple product(from above step) in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> + <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="{{simpleProductEnabledFlat.name}}" stepKey="fillSimpleProductNameInNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{simpleProductEnabledFlat.sku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedSimpleProductVisibleInGrid"/> + <waitForPageLoad stepKey="waitUntilSimpleProductPageIsOpened"/> + + <!-- Verify customer see updated simple product in the product form page --> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductEnabledFlat.name}}" stepKey="seeSimpleProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductEnabledFlat.sku}}" stepKey="seeSimpleProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductEnabledFlat.price}}" stepKey="seeSimpleProductPrice"/> + <seeInField selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{simpleProductEnabledFlat.productTaxClass}}" stepKey="seeProductTaxClass"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductEnabledFlat.quantity}}" stepKey="seeSimpleProductQuantity"/> + <click selector="{{AdminProductFormSection.advancedInventoryLink}}" stepKey="clickTheAdvancedInventoryLink"/> + <waitForPageLoad stepKey="waitForAdvancedInventoryPageLoad"/> + <see selector="{{AdminProductFormAdvancedInventorySection.manageStock}}" userInput="No" stepKey="seeManageStock"/> + <click selector="{{AdminProductFormAdvancedInventorySection.advancedInventoryCloseButton}}" stepKey="clickDoneButtonOnAdvancedInventory"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{simpleProductEnabledFlat.status}}" stepKey="seeSimpleProductStockStatus"/> + <seeInField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductEnabledFlat.weight}}" stepKey="seeSimpleProductWeight"/> + <seeInField selector="{{AdminProductFormSection.productWeightSelect}}" userInput="{{simpleProductEnabledFlat.weightSelect}}" stepKey="seeSimpleProductWeightSelect"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> + <see selector="{{AdminProductFormSection.selectMultipleCategories}}" userInput="$$categoryEntity.name$$" stepKey="seeSelectedCategories" /> + <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductEnabledFlat.visibility}}" stepKey="seeVisibility"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductEnabledFlat.urlKey}}" stepKey="seeUrlKey"/> + + <!--Verify customer see updated simple product link on category page --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{simpleProductEnabledFlat.name}}" stepKey="seeSimpleProductNameOnCategoryPage"/> + + <!-- Verify customer see updated simple product (from the above step) on the storefront page --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductEnabledFlat.urlKey)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{simpleProductEnabledFlat.name}}" stepKey="seeSimpleProductNameOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{simpleProductEnabledFlat.price}}" stepKey="seeSimpleProductPriceOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{simpleProductEnabledFlat.sku}}" stepKey="seeSimpleProductSkuOnStoreFrontPage"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="productStockAvailableStatus"/> + <assertEquals stepKey="assertStockAvailableOnProductPage"> + <expectedResult type="string">{{simpleProductEnabledFlat.storefrontStatus}}</expectedResult> + <actualResult type="variable">productStockAvailableStatus</actualResult> + </assertEquals> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="productPriceAmount"/> + <assertEquals stepKey="assertOldPriceTextOnProductPage"> + <expectedResult type="string">${{simpleProductEnabledFlat.price}}</expectedResult> + <actualResult type="variable">productPriceAmount</actualResult> + </assertEquals> + + <!--Verify customer see updated simple product link on magento storefront page and is searchable by sku --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductEnabledFlat.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{simpleProductEnabledFlat.sku}}" stepKey="fillSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{simpleProductEnabledFlat.name}}" stepKey="seeSimpleProductNameOnMagentoStorefrontPage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml new file mode 100644 index 0000000000000..c163e7bfa7ab9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest"> + <annotations> + <stories value="Update Simple Product"/> + <title value="Update Simple Product with Regular Price (In Stock) Not Visible Individually"/> + <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) Not Visible Individually"/> + <testCaseId value="MC-10803"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{simpleProductNotVisibleIndividually.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product with regular price(in stock) --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductNotVisibleIndividually.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductNotVisibleIndividually.sku}}" stepKey="fillSimpleProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductNotVisibleIndividually.price}}" stepKey="fillSimpleProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductNotVisibleIndividually.quantity}}" stepKey="fillSimpleProductQuantity"/> + <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{simpleProductNotVisibleIndividually.status}}" stepKey="selectStockStatusInStock"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductNotVisibleIndividually.weight}}" stepKey="fillSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory" /> + <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory"/> + <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductNotVisibleIndividually.visibility}}" stepKey="selectVisibility"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductNotVisibleIndividually.urlKey}}" stepKey="fillSimpleProductUrlKey"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!-- Search updated simple product(from above step) in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> + <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="{{simpleProductNotVisibleIndividually.name}}" stepKey="fillSimpleProductNameInNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{simpleProductNotVisibleIndividually.sku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedSimpleProductVisibleInGrid"/> + <waitForPageLoad stepKey="waitUntilSimpleProductPageIsOpened"/> + + <!-- Verify customer see updated simple product in the product form page --> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductNotVisibleIndividually.name}}" stepKey="seeSimpleProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductNotVisibleIndividually.sku}}" stepKey="seeSimpleProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductNotVisibleIndividually.price}}" stepKey="seeSimpleProductPrice"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductNotVisibleIndividually.quantity}}" stepKey="seeSimpleProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{simpleProductNotVisibleIndividually.status}}" stepKey="seeSimpleProductStockStatus"/> + <seeInField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductNotVisibleIndividually.weight}}" stepKey="seeSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> + <see selector="{{AdminProductFormSection.selectMultipleCategories}}" userInput="$$categoryEntity.name$$" stepKey="seeSelectedCategories" /> + <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductNotVisibleIndividually.visibility}}" stepKey="seeSimpleProductVisibility"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> + <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductNotVisibleIndividually.urlKey}}" stepKey="seeSimpleProductUrlKey"/> + + <!--Verify customer don't see updated simple product link on magento storefront page --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductNotVisibleIndividually.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{simpleProductNotVisibleIndividually.sku}}" stepKey="fillSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <dontSee selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{simpleProductNotVisibleIndividually.name}}" stepKey="dontSeeSimpleProductNameOnMagentoStorefrontPage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml new file mode 100644 index 0000000000000..8476afe5263e6 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.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="AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest"> + <annotations> + <stories value="Update Simple Product"/> + <title value="Update Simple Product with Regular Price (In Stock) Unassign from Category"/> + <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) Unassign from Category"/> + <testCaseId value="MC-10817"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="_defaultProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Search default simple product in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillDefaultSimpleProductName"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product by unselecting categories --> + <scrollTo selector="{{AdminProductFormSection.productStockStatus}}" stepKey="scroll"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <click selector="{{AdminProductFormSection.unselectCategories($$initialCategoryEntity.name$$)}}" stepKey="unselectCategories"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategory"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!--Search default simple product in the grid page --> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="OpenCategoryCatalogPage"/> + <waitForPageLoad stepKey="waitForCategoryCatalogPage"/> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickExpandTree"/> + <waitForPageLoad stepKey="waitForCategoryToLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$initialCategoryEntity.name$$)}}" stepKey="selectCategory"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="clickAdminCategoryProductSection"/> + <waitForPageLoad stepKey="waitForSectionHeaderToLoad"/> + <dontSee selector="{{AdminCategoryProductsGridSection.rowProductName($$initialSimpleProduct.name$$)}}" stepKey="dontSeeProductNameOnCategoryCatalogPage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml new file mode 100644 index 0000000000000..a7c7b66171983 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.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="AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest"> + <annotations> + <stories value="Update Simple Product"/> + <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"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{simpleProductRegularPrice245InStock.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product with regular price(in stock) --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPrice245InStock.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPrice245InStock.sku}}" stepKey="fillSimpleProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPrice245InStock.price}}" stepKey="fillSimpleProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPrice245InStock.quantity}}" stepKey="fillSimpleProductQuantity"/> + <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{simpleProductRegularPrice245InStock.status}}" stepKey="selectStockStatusInStock"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPrice245InStock.weight}}" stepKey="fillSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory" /> + <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory" /> + <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductRegularPrice245InStock.visibility}}" stepKey="selectVisibility"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice245InStock.urlKey}}" stepKey="fillSimpleProductUrlKey"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!-- Search updated simple product(from above step) in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> + <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="{{simpleProductRegularPrice245InStock.name}}" stepKey="fillSimpleProductNameInNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{simpleProductRegularPrice245InStock.sku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedSimpleProductVisibleInGrid"/> + <waitForPageLoad stepKey="waitUntilSimpleProductPageIsOpened"/> + + <!-- Verify customer see updated simple product in the product form page --> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPrice245InStock.name}}" stepKey="seeSimpleProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPrice245InStock.sku}}" stepKey="seeSimpleProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPrice245InStock.price}}" stepKey="seeSimpleProductPrice"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPrice245InStock.quantity}}" stepKey="seeSimpleProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{simpleProductRegularPrice245InStock.status}}" stepKey="seeSimpleProductStockStatus"/> + <seeInField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPrice245InStock.weight}}" stepKey="seeSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> + <see selector="{{AdminProductFormSection.selectMultipleCategories}}" userInput="$$categoryEntity.name$$" stepKey="selectedCategories" /> + <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductRegularPrice245InStock.visibility}}" stepKey="seeVisibility"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice245InStock.urlKey}}" stepKey="seeUrlKey"/> + + <!--Verify customer see updated simple product link on category page --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{simpleProductRegularPrice245InStock.name}}" stepKey="seeSimpleProductNameOnCategoryPage"/> + + <!-- Verify customer see updated simple product (from the above step) on the storefront page --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductRegularPrice245InStock.urlKey)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{simpleProductRegularPrice245InStock.name}}" stepKey="seeSimpleProductNameOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{simpleProductRegularPrice245InStock.price}}" stepKey="seeSimpleProductPriceOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{simpleProductRegularPrice245InStock.sku}}" stepKey="seeSimpleProductSkuOnStoreFrontPage"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="productStockAvailableStatus"/> + <assertEquals stepKey="assertStockAvailableOnProductPage"> + <expectedResult type="string">{{simpleProductRegularPrice245InStock.storefrontStatus}}</expectedResult> + <actualResult type="variable">productStockAvailableStatus</actualResult> + </assertEquals> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="productPriceAmount"/> + <assertEquals stepKey="assertOldPriceTextOnProductPage"> + <expectedResult type="string">${{simpleProductRegularPrice245InStock.price}}</expectedResult> + <actualResult type="variable">productPriceAmount</actualResult> + </assertEquals> + + <!--Verify customer see updated simple product link on magento storefront page and is searchable by sku --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductRegularPrice245InStock.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{simpleProductRegularPrice245InStock.sku}}" stepKey="fillSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{simpleProductRegularPrice245InStock.name}}" stepKey="seeSimpleProductNameOnStorefrontPage"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml new file mode 100644 index 0000000000000..675206283a4ef --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.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="AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest"> + <annotations> + <stories value="Update Simple Product"/> + <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"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{simpleProductRegularPrice32501InStock.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in the grid --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product with regular price(in stock) --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPrice32501InStock.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPrice32501InStock.sku}}" stepKey="fillSimpleProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPrice32501InStock.price}}" stepKey="fillSimpleProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPrice32501InStock.quantity}}" stepKey="fillSimpleProductQuantity"/> + <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{simpleProductRegularPrice32501InStock.status}}" stepKey="selectStockStatusInStock"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPrice32501InStock.weight}}" stepKey="fillSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory"/> + <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory"/> + <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductRegularPrice32501InStock.visibility}}" stepKey="selectVisibility"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice32501InStock.urlKey}}" stepKey="fillUrlKey"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!-- Search updated simple product(from above step) in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> + <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="{{simpleProductRegularPrice32501InStock.name}}" stepKey="fillSimpleProductNameInNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{simpleProductRegularPrice32501InStock.sku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedSimpleProductVisibleInGrid"/> + <waitForPageLoad stepKey="waitUntilSimpleProductPageIsOpened"/> + + <!-- Verify customer see updated simple product in the product form page --> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPrice32501InStock.name}}" stepKey="seeSimpleProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPrice32501InStock.sku}}" stepKey="seeSimpleProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPrice32501InStock.price}}" stepKey="seeSimpleProductPrice"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPrice32501InStock.quantity}}" stepKey="seeSimpleProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{simpleProductRegularPrice32501InStock.status}}" stepKey="seeSimpleProductStockStatus"/> + <seeInField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPrice32501InStock.weight}}" stepKey="seeSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> + <see selector="{{AdminProductFormSection.selectMultipleCategories}}" userInput="$$categoryEntity.name$$" stepKey="selectedCategories" /> + <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductRegularPrice32501InStock.visibility}}" stepKey="seeVisibility"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice32501InStock.urlKey}}" stepKey="seeUrlKey"/> + + <!--Verify customer see updated simple product link on category page --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{simpleProductRegularPrice32501InStock.name}}" stepKey="seeSimpleProductNameOnCategoryPage"/> + + <!-- Verify customer see updated simple product (from the above step) on the storefront page --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductRegularPrice32501InStock.urlKey)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{simpleProductRegularPrice32501InStock.name}}" stepKey="seeSimpleProductNameOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{simpleProductRegularPrice32501InStock.price}}" stepKey="seeSimpleProductPriceOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{simpleProductRegularPrice32501InStock.sku}}" stepKey="seeProductSku"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="productStockAvailableStatus"/> + <assertEquals stepKey="assertStockAvailableOnProductPage"> + <expectedResult type="string">{{simpleProductRegularPrice32501InStock.storefrontStatus}}</expectedResult> + <actualResult type="variable">productStockAvailableStatus</actualResult> + </assertEquals> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="productPriceAmount"/> + <assertEquals stepKey="assertOldPriceTextOnProductPage"> + <expectedResult type="string">${{simpleProductRegularPrice32501InStock.price}}</expectedResult> + <actualResult type="variable">productPriceAmount</actualResult> + </assertEquals> + + <!--Verify customer don't see updated simple product link on magento storefront page and is searchable by sku --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductRegularPrice32501InStock.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{simpleProductRegularPrice32501InStock.sku}}" stepKey="fillSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <dontSee selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{simpleProductRegularPrice32501InStock.name}}" stepKey="dontSeeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml new file mode 100644 index 0000000000000..4b54b0e7c6fcf --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.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="AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest"> + <annotations> + <stories value="Update Simple Product"/> + <title value="Update Simple Product with Regular Price (In Stock) Visible in Search Only"/> + <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (In Stock) Visible in Search Only"/> + <testCaseId value="MC-10805"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{simpleProductRegularPrice325InStock.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in the grid --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product with regular price(in stock) --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPrice325InStock.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPrice325InStock.sku}}" stepKey="fillSimpleProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPrice325InStock.price}}" stepKey="fillSimpleProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPrice325InStock.quantity}}" stepKey="fillSimpleProductQuantity"/> + <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{simpleProductRegularPrice325InStock.status}}" stepKey="selectStockStatusInStock"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPrice325InStock.weight}}" stepKey="fillSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory" /> + <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory" /> + <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductRegularPrice325InStock.visibility}}" stepKey="selectVisibility"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice325InStock.urlKey}}" stepKey="fillUrlKey"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!-- Search updated simple product(from above step) in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> + <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="{{simpleProductRegularPrice325InStock.name}}" stepKey="fillSimpleProductNameInNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{simpleProductRegularPrice325InStock.sku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedSimpleProductVisibleInGrid"/> + <waitForPageLoad stepKey="waitUntilSimpleProductPageIsOpened"/> + + <!-- Verify customer see updated simple product in the product form page --> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPrice325InStock.name}}" stepKey="seeSimpleProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPrice325InStock.sku}}" stepKey="seeSimpleProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPrice325InStock.price}}" stepKey="seeSimpleProductPrice"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPrice325InStock.quantity}}" stepKey="seeSimpleProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{simpleProductRegularPrice325InStock.status}}" stepKey="seeSimpleProductStockStatus"/> + <seeInField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPrice325InStock.weight}}" stepKey="seeSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> + <see selector="{{AdminProductFormSection.selectMultipleCategories}}" userInput="$$categoryEntity.name$$" stepKey="selectedCategories" /> + <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="{{simpleProductRegularPrice325InStock.visibility}}" stepKey="seeVisibility"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice325InStock.urlKey}}" stepKey="seeUrlKey"/> + + <!--Verify customer don't see updated simple product link on category page --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <dontSee selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{simpleProductRegularPrice325InStock.name}}" stepKey="dontSeeSimpleProductNameOnCategoryPage"/> + + <!-- Verify customer see updated simple product (from the above step) on the storefront page --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductRegularPrice325InStock.urlKey)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{simpleProductRegularPrice325InStock.name}}" stepKey="seeSimpleProductNameOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{simpleProductRegularPrice325InStock.price}}" stepKey="seeSimpleProductPriceOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{simpleProductRegularPrice325InStock.sku}}" stepKey="seeProductSku"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="productStockAvailableStatus"/> + <assertEquals stepKey="assertStockAvailableOnProductPage"> + <expectedResult type="string">{{simpleProductRegularPrice325InStock.storefrontStatus}}</expectedResult> + <actualResult type="variable">productStockAvailableStatus</actualResult> + </assertEquals> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="productPriceAmount"/> + <assertEquals stepKey="assertOldPriceTextOnProductPage"> + <expectedResult type="string">${{simpleProductRegularPrice325InStock.price}}</expectedResult> + <actualResult type="variable">productPriceAmount</actualResult> + </assertEquals> + + <!--Verify customer see updated simple product link on magento storefront page and is searchable by sku --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductRegularPrice325InStock.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{simpleProductRegularPrice325InStock.sku}}" stepKey="fillSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{simpleProductRegularPrice325InStock.name}}" stepKey="seeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml new file mode 100644 index 0000000000000..2ee993d2af696 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest"> + <annotations> + <stories value="Update Simple Product"/> + <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"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{simpleProductRegularPriceCustomOptions.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in the grid --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product with regular price --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPriceCustomOptions.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPriceCustomOptions.sku}}" stepKey="fillSimpleProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPriceCustomOptions.price}}" stepKey="fillSimpleProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPriceCustomOptions.quantity}}" stepKey="fillSimpleProductQuantity"/> + <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{simpleProductRegularPriceCustomOptions.status}}" stepKey="selectStockStatusInStock"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPriceCustomOptions.weight}}" stepKey="fillSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory"/> + <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory"/> + <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPriceCustomOptions.urlKey}}" stepKey="fillUrlKey"/> + <click selector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" stepKey="clickAdminProductCustomizableOption"/> + <!-- Create simple product with customizable option --> + <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOptionButton"/> + <waitForPageLoad stepKey="waitForDataToLoad"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.optionTitleInput('0')}}" userInput="{{simpleProductCustomizableOption.title}}" stepKey="fillOptionTitle"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionTypeDropDown('1')}}" stepKey="selectOptionTypeDropDown"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionTypeItem('1', simpleProductCustomizableOption.type)}}" stepKey="selectOptionFieldFromDropDown"/> + <checkOption selector="{{AdminProductCustomizableOptionsSection.requiredCheckBox('0')}}" stepKey="checkRequiredCheckBox"/> + <click selector="{{AdminProductCustomizableOptionsSection.addValue}}" stepKey="clickAddValueButton"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle(simpleProductCustomizableOption.title,'0')}}" userInput="{{simpleProductCustomizableOption.option_0_title}}" stepKey="fillOptionTitleForCustomizableOption"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice(simpleProductCustomizableOption.title,'0')}}" userInput="{{simpleProductCustomizableOption.option_0_price}}" stepKey="fillOptionPrice"/> + <selectOption selector="{{AdminProductCustomizableOptionsSection.clickSelectPriceType(simpleProductCustomizableOption.title,'0')}}" userInput="{{simpleProductCustomizableOption.option_0_price_type}}" stepKey="selectOptionPriceType"/> + <fillField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueSku(simpleProductCustomizableOption.title,'0')}}" userInput="{{simpleProductCustomizableOption.option_0_sku}}" stepKey="fillOptionSku"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!--Verify customer see success message--> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!--Search updated simple product(from above step) in the grid page--> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> + <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="{{simpleProductRegularPriceCustomOptions.name}}" stepKey="fillSimpleProductNameInNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{simpleProductRegularPriceCustomOptions.sku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedSimpleProductVisibleInGrid"/> + <waitForPageLoad stepKey="waitUntilSimpleProductPageIsOpened"/> + + <!-- Verify customer see updated simple product in the product form page --> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPriceCustomOptions.name}}" stepKey="seeSimpleProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPriceCustomOptions.sku}}" stepKey="seeSimpleProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPriceCustomOptions.price}}" stepKey="seeSimpleProductPrice"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPriceCustomOptions.quantity}}" stepKey="seeSimpleProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{simpleProductRegularPriceCustomOptions.status}}" stepKey="seeSimpleProductStockStatus"/> + <seeInField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPriceCustomOptions.weight}}" stepKey="seeSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> + <see selector="{{AdminProductFormSection.selectMultipleCategories}}" userInput="$$categoryEntity.name$$" stepKey="selectedCategories" /> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPriceCustomOptions.urlKey}}" stepKey="seeUrlKey"/> + <click selector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" stepKey="clickAdminProductCustomizableOptionToSeeValues"/> + <!-- Verify simple product with customizable options --> + <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOptionButtonForCustomizableOption"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <seeInField selector="{{AdminProductCustomizableOptionsSection.optionTitleInput('0')}}" userInput="{{simpleProductCustomizableOption.title}}" stepKey="seeOptionTitleForCustomizableOption"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionTypeDropDown('1')}}" stepKey="selectOptionTypeDropDownForCustomizableOption"/> + <click selector="{{AdminProductCustomizableOptionsSection.optionTypeItem('1', simpleProductCustomizableOption.type)}}" stepKey="selectOptionFieldFromDropDownForCustomizableOption"/> + <checkOption selector="{{AdminProductCustomizableOptionsSection.requiredCheckBox('0')}}" stepKey="checkRequiredCheckBoxForTheThirdDataSet"/> + <seeInField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueTitle(simpleProductCustomizableOption.title,'0')}}" userInput="{{simpleProductCustomizableOption.option_0_title}}" stepKey="seeOptionTitle"/> + <seeInField selector="{{AdminProductCustomizableOptionsSection.fillOptionValuePrice(simpleProductCustomizableOption.title,'0')}}" userInput="{{simpleProductCustomizableOption.option_0_price}}" stepKey="seeOptionPrice"/> + <seeOptionIsSelected selector="{{AdminProductCustomizableOptionsSection.clickSelectPriceType(simpleProductCustomizableOption.title,'0')}}" userInput="{{simpleProductCustomizableOption.option_0_price_type}}" stepKey="selectOptionValuePriceType"/> + <seeInField selector="{{AdminProductCustomizableOptionsSection.fillOptionValueSku(simpleProductCustomizableOption.title,'0')}}" userInput="{{simpleProductCustomizableOption.option_0_sku}}" stepKey="seeOptionSku"/> + + <!-- Verify customer see updated simple product (from the above step) on the storefront page --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductRegularPriceCustomOptions.urlKey)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{simpleProductRegularPriceCustomOptions.name}}" stepKey="seeSimpleProductNameOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{simpleProductRegularPriceCustomOptions.price}}" stepKey="seeSimpleProductPriceOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{simpleProductRegularPriceCustomOptions.sku}}" stepKey="seeSimpleProductSkuOnStoreFrontPage"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="productStockAvailableStatus"/> + <assertEquals stepKey="assertStockAvailableOnProductPage"> + <expectedResult type="string">{{simpleProductRegularPriceCustomOptions.storefrontStatus}}</expectedResult> + <actualResult type="variable">productStockAvailableStatus</actualResult> + </assertEquals> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="productPriceAmount"/> + <assertEquals stepKey="assertOldPriceTextOnProductPage"> + <expectedResult type="string">${{simpleProductRegularPriceCustomOptions.price}}</expectedResult> + <actualResult type="variable">productPriceAmount</actualResult> + </assertEquals> + + <!--Verify customer see customizable options are Required --> + <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomSelect(simpleProductCustomizableOption.title)}}" stepKey="verifyFistCustomOptionIsRequired"/> + <!--Verify customer see customizable option titles and prices --> + <grabAttributeFrom userInput="for" selector="{{StorefrontProductInfoMainSection.customOptionLabel(simpleProductCustomizableOption.title)}}" stepKey="simpleOptionId"/> + <grabMultiple selector="{{StorefrontProductInfoMainSection.customSelectOptions({$simpleOptionId})}}" stepKey="grabFourthOptions"/> + <assertEquals stepKey="assertFourthSelectOptions"> + <actualResult type="variable">grabFourthOptions</actualResult> + <expectedResult type="array">['-- Please Select --', {{simpleProductCustomizableOption.option_0_title}} +$98.00]</expectedResult> + </assertEquals> + + <!-- Verify added Product in cart --> + <selectOption selector="{{StorefrontProductPageSection.customOptionDropDown}}" userInput="{{simpleProductCustomizableOption.option_0_title}} +$98.00" stepKey="selectCustomOption"/> + <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="1" stepKey="fillProductQuantity"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="clickOnAddToCartButton"/> + <waitForPageLoad stepKey="waitForProductToAddInCart"/> + <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeYouAddedSimpleprod4ToYourShoppingCartSuccessSaveMessage"/> + <seeElement selector="{{StorefrontMinicartSection.quantity(1)}}" stepKey="seeAddedProductQuantityInCart"/> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> + <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{simpleProductRegularPriceCustomOptions.name}}" stepKey="seeProductNameInMiniCart"/> + <see selector="{{StorefrontMinicartSection.miniCartItemsText}}" userInput="{{simpleProductRegularPriceCustomOptions.storefront_new_cartprice}}" stepKey="seeProductPriceInMiniCart"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml new file mode 100644 index 0000000000000..ea575bdb2771e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.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="AdminUpdateSimpleProductWithRegularPriceOutOfStockTest"> + <annotations> + <stories value="Update Simple Product"/> + <title value="Update Simple Product with Regular Price (Out of Stock)"/> + <description value="Test log in to Update Simple Product and Update Simple Product with Regular Price (Out of Stock)"/> + <testCaseId value="MC-10806"/> + <severity value="CRITICAL"/> + <group value="catalog"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> + <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> + <requiredEntity createDataKey="initialCategoryEntity"/> + </createData> + <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + </before> + <after> + <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> + <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> + <actionGroup ref="deleteProductBySku" stepKey="deleteCreatedProduct"> + <argument name="sku" value="{{simpleProductRegularPrice32503OutOfStock.sku}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Search default simple product in the grid --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> + <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> + <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> + <waitForPageLoad stepKey="waitForProductSearch"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + + <!-- Update simple product with regular price(out of stock) --> + <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPrice32503OutOfStock.name}}" stepKey="fillSimpleProductName"/> + <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPrice32503OutOfStock.sku}}" stepKey="fillSimpleProductSku"/> + <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPrice32503OutOfStock.price}}" stepKey="fillSimpleProductPrice"/> + <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPrice32503OutOfStock.quantity}}" stepKey="fillSimpleProductQuantity"/> + <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{simpleProductRegularPrice32503OutOfStock.status}}" stepKey="selectStockStatusInStock"/> + <fillField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPrice32503OutOfStock.weight}}" stepKey="fillSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory"/> + <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> + <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory"/> + <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> + <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> + <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice32503OutOfStock.urlKey}}" stepKey="fillUrlKey"/> + <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> + <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> + <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> + <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + + <!-- Search updated simple product(from above step) in the grid page --> + <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> + <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="{{simpleProductRegularPrice32503OutOfStock.name}}" stepKey="fillSimpleProductNameInNameFilter"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{simpleProductRegularPrice32503OutOfStock.sku}}" stepKey="fillProductSku"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedSimpleProductVisibleInGrid"/> + <waitForPageLoad stepKey="waitUntilSimpleProductPageIsOpened"/> + + <!-- Verify customer see updated simple product in the product form page --> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductRegularPrice32503OutOfStock.name}}" stepKey="seeSimpleProductName"/> + <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductRegularPrice32503OutOfStock.sku}}" stepKey="seeSimpleProductSku"/> + <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductRegularPrice32503OutOfStock.price}}" stepKey="seeSimpleProductPrice"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{simpleProductRegularPrice32503OutOfStock.quantity}}" stepKey="seeSimpleProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{simpleProductRegularPrice32503OutOfStock.status}}" stepKey="seeSimpleProductStockStatus"/> + <seeInField selector="{{AdminProductFormSection.productWeight}}" userInput="{{simpleProductRegularPrice32503OutOfStock.weight}}" stepKey="seeSimpleProductWeight"/> + <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> + <see selector="{{AdminProductFormSection.selectMultipleCategories}}" userInput="$$categoryEntity.name$$" stepKey="selectedCategories" /> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> + <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPrice32503OutOfStock.urlKey}}" stepKey="seeUrlKey"/> + + <!--Verify customer don't see updated simple product link on category page --> + <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> + <waitForPageLoad stepKey="waitForCategoryPageLoad"/> + <dontSee selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{simpleProductRegularPrice32503OutOfStock.name}}" stepKey="dontSeeSimpleProductNameOnCategoryPage"/> + + <!-- Verify customer see updated simple product (from the above step) on the storefront page --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductRegularPrice32503OutOfStock.urlKey)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{simpleProductRegularPrice32503OutOfStock.name}}" stepKey="seeSimpleProductNameOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{simpleProductRegularPrice32503OutOfStock.price}}" stepKey="seeSimpleProductPriceOnStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{simpleProductRegularPrice32503OutOfStock.sku}}" stepKey="seeSimpleProductSkuOnStoreFrontPage"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="productStockAvailableStatus"/> + <assertEquals stepKey="assertStockAvailableOnProductPage"> + <expectedResult type="string">{{simpleProductRegularPrice32503OutOfStock.storefrontStatus}}</expectedResult> + <actualResult type="variable">productStockAvailableStatus</actualResult> + </assertEquals> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="productPriceAmount"/> + <assertEquals stepKey="assertOldPriceTextOnProductPage"> + <expectedResult type="string">${{simpleProductRegularPrice32503OutOfStock.price}}</expectedResult> + <actualResult type="variable">productPriceAmount</actualResult> + </assertEquals> + + <!--Verify customer don't see updated simple product link on magento storefront page and is searchable by sku --> + <amOnPage url="{{StorefrontProductPage.url(simpleProductRegularPrice32503OutOfStock.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> + <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> + <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{simpleProductRegularPrice32503OutOfStock.sku}}" stepKey="fillSimpleProductSkuInSearchTextBox"/> + <waitForPageLoad stepKey="waitForSearchTextBox"/> + <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> + <waitForPageLoad stepKey="waitForSearch"/> + <dontSee selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{simpleProductRegularPrice32503OutOfStock.name}}" stepKey="dontSeeProductName"/> + </test> +</tests> diff --git a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml index 9e04bbb12a796..9e5bde9a2be49 100644 --- a/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml +++ b/app/code/Magento/Search/Test/Mftf/Section/StorefrontQuickSearchResultsSection.xml @@ -14,6 +14,6 @@ <element name="productLink" type="select" selector="a[class='product-item-link']"/> <element name="asLowAsLabel" type="text" selector=".minimal-price-link > span"/> <element name="textArea" type="text" selector="li[class='item']"/> - <element name="regularPrice" type="text" selector="li[class='item']"/> + <element name="regularPrice" type="text" selector="//span[@class='price-wrapper ']/span[@class='price']"/> </section> </sections> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml index 31bbe7550e5a1..bef2ab27dafc0 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/CreateCustomStoreViewActionGroup.xml @@ -21,4 +21,21 @@ <waitForElementVisible selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" stepKey="waitForAcceptNewStoreViewCreationButton" /> <conditionalClick selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" dependentSelector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" visible="true" stepKey="clickAcceptNewStoreViewCreationButton"/> </actionGroup> + <actionGroup name="CreateStoreView"> + <arguments> + <argument name="storeView" defaultValue="customStore"/> + <argument name="storeGroupName" defaultValue="_defaultStoreGroup.name"/> + <argument name="storeViewStatus" defaultValue="_defaultStore.is_active"/> + </arguments> + <amOnPage url="{{AdminSystemStoreViewPage.url}}" stepKey="amOnAdminSystemStoreViewPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <selectOption userInput="{{storeGroupName}}" selector="{{AdminNewStoreSection.storeGrpDropdown}}" stepKey="selectStoreGroup"/> + <fillField userInput="{{storeView.name}}" selector="{{AdminNewStoreSection.storeNameTextField}}" stepKey="fillStoreViewName"/> + <fillField userInput="{{storeView.code}}" selector="{{AdminNewStoreSection.storeCodeTextField}}" stepKey="fillStoreViewCode"/> + <selectOption userInput="{{storeViewStatus}}" selector="{{AdminNewStoreSection.statusDropdown}}" stepKey="selectStoreViewStatus"/> + <click selector="{{AdminStoresMainActionsSection.saveButton}}" stepKey="clickSaveStoreViewButton"/> + <waitForElementVisible selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" stepKey="waitForAcceptNewStoreViewCreationButton" /> + <conditionalClick selector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" dependentSelector="{{AdminNewStoreSection.acceptNewStoreViewCreation}}" visible="true" stepKey="clickAcceptNewStoreViewCreationButton"/> + <see userInput="You saved the store view." stepKey="seeSavedMessage"/> + </actionGroup> </actionGroups> From 0654c3226c1da6e67ceb830e333f1dd31aa9b8d1 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Thu, 7 Feb 2019 23:40:33 +0200 Subject: [PATCH 059/254] graphQl-352: is email available --- .../Customer/IsEmailAvailableDataProvider.php | 40 ++++++++++++++ .../Model/Resolver/IsEmailAvailable.php | 55 +++++++++++++++++++ .../CustomerGraphQl/etc/schema.graphqls | 7 +++ .../GraphQl/Customer/IsEmailAvailableTest.php | 50 +++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/IsEmailAvailableDataProvider.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/IsEmailAvailableTest.php diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/IsEmailAvailableDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Customer/IsEmailAvailableDataProvider.php new file mode 100644 index 0000000000000..9ec4ab9377ead --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/IsEmailAvailableDataProvider.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CustomerGraphQl\Model\Customer; + +use Magento\Customer\Api\AccountManagementInterface; + +/** + * Is Customer Email Available checker + */ +class IsEmailAvailableDataProvider +{ + /** + * @var AccountManagementInterface + */ + private $accountManagement; + + /** + * @param AccountManagementInterface $accountManagement + */ + public function __construct(AccountManagementInterface $accountManagement) + { + $this->accountManagement = $accountManagement; + } + + /** + * Check is Email available + * + * @param string $email + * @return bool + */ + public function execute(string $email): bool + { + return $this->accountManagement->isEmailAvailable($email); + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php new file mode 100644 index 0000000000000..a5edb78e67bfd --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CustomerGraphQl\Model\Resolver; + +use Magento\CustomerGraphQl\Model\Customer\IsEmailAvailableDataProvider; +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; + +/** + * Is Customer Email Available + */ +class IsEmailAvailable implements ResolverInterface +{ + /** + * @var IsEmailAvailableDataProvider + */ + private $isEmailAvailableDataProvider; + + /** + * @param IsEmailAvailableDataProvider $isEmailAvailableDataProvider + */ + public function __construct( + IsEmailAvailableDataProvider $isEmailAvailableDataProvider + ) { + $this->isEmailAvailableDataProvider = $isEmailAvailableDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $email = $args['email'] ?? null; + if (!$email) { + throw new GraphQlInputException(__('"Email should be specified')); + } + $isEmailAvailable = $this->isEmailAvailableDataProvider->execute($email); + + return [ + 'is_email_available' => $isEmailAvailable + ]; + } +} diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index f4a417fe2f017..139ac80be8429 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -3,6 +3,9 @@ type Query { customer: Customer @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Customer") @doc(description: "The customer query returns information about a customer account") + isEmailAvailable ( + email: String! @doc(description: "The new customer email") + ): IsEmailAvailableOutput @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\IsEmailAvailable") } type Mutation { @@ -126,6 +129,10 @@ type CustomerAddressAttribute { value: String @doc(description: "Attribute value") } +type IsEmailAvailableOutput { + is_email_available: Boolean @doc(description: "Is email availabel value") +} + enum CountryCodeEnum @doc(description: "The list of countries codes") { AF @doc(description: "Afghanistan") AX @doc(description: "Åland Islands") diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/IsEmailAvailableTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/IsEmailAvailableTest.php new file mode 100644 index 0000000000000..37693fbba7fef --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/IsEmailAvailableTest.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Customer; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +class IsEmailAvailableTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testEmailNotAvailable() + { + $query = + <<<QUERY +{ + isEmailAvailable(email: "customer@example.com") { + is_email_available + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('isEmailAvailable', $response); + self::assertArrayHasKey('is_email_available', $response['isEmailAvailable']); + self::assertFalse($response['isEmailAvailable']['is_email_available']); + } + + public function testEmailAvailable() + { + $query = + <<<QUERY +{ + isEmailAvailable(email: "customer@example.com") { + is_email_available + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('isEmailAvailable', $response); + self::assertArrayHasKey('is_email_available', $response['isEmailAvailable']); + self::assertTrue($response['isEmailAvailable']['is_email_available']); + } +} From 29c858f7f12577287523a81d2b890caa6a69a427 Mon Sep 17 00:00:00 2001 From: Maria Kovdrysh <kovdrysh@adobe.com> Date: Fri, 8 Feb 2019 13:26:31 -0600 Subject: [PATCH 060/254] MC-4409: Convert UpdateProductAttributeEntityTest to MFTF --- .../AdminProductAttributeSetActionGroup.xml | 9 +++ .../Test/Mftf/Data/ProductAttributeData.xml | 42 +++++++++++ .../AdminCreateProductAttributeSection.xml | 2 + .../AdminProductAttributeSetGridSection.xml | 1 + ...ibleInStorefrontAdvancedSearchFormTest.xml | 71 +++++++++++++++++++ ...ibleInStorefrontAdvancedSearchFormTest.xml | 71 +++++++++++++++++++ 6 files changed, 196 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml index a3b4203b7a69e..458fb9a509559 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml @@ -54,4 +54,13 @@ <waitForPageLoad stepKey="waitForUserInput"/> <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="searchForAttributeFromTheGrid"/> </actionGroup> + <actionGroup name="FilterProductAttributeSetGridByAttributeSetName"> + <arguments> + <argument name="name" type="string"/> + </arguments> + <click selector="{{AdminProductAttributeSetGridSection.resetFilter}}" stepKey="clickResetButton"/> + <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="{{name}}" stepKey="filterByName"/> + <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickSearch"/> + <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index 3d21d1c9efc92..866cff65d8bc5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -73,6 +73,27 @@ <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> + <entity name="productDropDownAttributeNotSearchable" type="ProductAttribute"> + <data key="attribute_code" unique="suffix">attribute</data> + <data key="frontend_input">select</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_visible_in_advanced_search">true</data> + <data key="is_visible_on_front">true</data> + <data key="is_filterable">true</data> + <data key="is_filterable_in_search">true</data> + <data key="used_in_product_listing">true</data> + <data key="is_used_for_promo_rules">true</data> + <data key="is_comparable">true</data> + <data key="is_used_in_grid">true</data> + <data key="is_visible_in_grid">true</data> + <data key="is_filterable_in_grid">true</data> + <data key="used_for_sort_by">true</data> + <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> + </entity> <entity name="productAttributeWithDropdownTwoOptions" type="ProductAttribute"> <data key="attribute_code">testattribute</data> <data key="frontend_input">select</data> @@ -115,6 +136,27 @@ <data key="used_for_sort_by">true</data> <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> </entity> + <entity name="productAttributeMultiselectTwoOptionsNotSearchable" type="ProductAttribute"> + <data key="attribute_code" unique="suffix">attribute</data> + <data key="frontend_input">multiselect</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_visible_in_advanced_search">true</data> + <data key="is_visible_on_front">true</data> + <data key="is_filterable">true</data> + <data key="is_filterable_in_search">true</data> + <data key="used_in_product_listing">true</data> + <data key="is_used_for_promo_rules">true</data> + <data key="is_comparable">true</data> + <data key="is_used_in_grid">true</data> + <data key="is_visible_in_grid">true</data> + <data key="is_filterable_in_grid">true</data> + <data key="used_for_sort_by">true</data> + <requiredEntity type="FrontendLabel">ProductAttributeFrontendLabel</requiredEntity> + </entity> <entity name="newsFromDate" type="ProductAttribute"> <data key="attribute_code">news_from_date</data> <data key="default_frontend_label">Set Product as New from Date</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 05be20b14acc0..09ccbc9929a2e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -81,5 +81,7 @@ <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/AdminProductAttributeSetGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml index b906e2fa9084b..3fad50adb771a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeSetGridSection.xml @@ -14,5 +14,6 @@ <element name="nthRow" type="block" selector="#setGrid_table tbody tr:nth-of-type({{var1}})" parameterized="true"/> <element name="AttributeSetName" type="text" selector="//td[contains(text(), '{{var1}}')]" parameterized="true"/> <element name="addAttributeSetBtn" type="button" selector="button.add-set" timeout="30"/> + <element name="resetFilter" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml new file mode 100644 index 0000000000000..c0d334861642d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.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="AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create product Dropdown attribute and check its visibility on frontend in Advanced Search form"/> + <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"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create product attribute with 2 options --> + <createData entity="productDropDownAttributeNotSearchable" stepKey="attribute"/> + <createData entity="productAttributeOption1" stepKey="option1"> + <requiredEntity createDataKey="attribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="option2"> + <requiredEntity createDataKey="attribute"/> + </createData> + <!-- Create product attribute set --> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <!-- Filter product attribute set by attribute set name --> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> + <actionGroup ref="FilterProductAttributeSetGridByAttributeSetName" stepKey="filterProductAttrSetGridByAttrSetName"> + <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> + </actionGroup> + <!-- Assert created attribute in an unassigned attributes --> + <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassignedAttr"/> + <!-- Assign attribute in the group --> + <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$attribute.attribute_code$$"/> + </actionGroup> + <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttributeInGroup"/> + <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> + <!-- Go to Product Attribute Grid page --> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="$$attribute.attribute_code$$" stepKey="fillAttrCodeField" /> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearchBtn" /> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="chooseFirstRow" /> + <!-- Change attribute property: Frontend Label --> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{productDropDownAttribute.attribute_code}}" stepKey="fillDefaultLabel"/> + <!-- Change attribute property: Use in Search >Yes --> + <scrollToTopOfPage stepKey="scrollToTabs"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" stepKey="waitTabLoad"/> + <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="Yes" stepKey="seeInSearch"/> + <!-- Change attribute property: Visible In Advanced Search >No --> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" stepKey="waitTabLoad2"/> + <selectOption selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" userInput="No" stepKey="dontSeeInAdvancedSearch"/> + <!-- Save the new product attributes --> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSave"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> + <!-- Flash cache --> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!-- Go to store's advanced catalog search page --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <dontSeeElement selector="{{StorefrontCatalogSearchAdvancedFormSection.AttributeByCode('$$attribute.attribute_code$$')}}" stepKey="dontSeeAttribute"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml new file mode 100644 index 0000000000000..b7fdb7bd67941 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.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="AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest"> + <annotations> + <features value="Catalog"/> + <stories value="Create Multiple Select product attribute and check its visibility in Advanced Search form"/> + <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"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create a multiple select product attribute with two options --> + <createData entity="productAttributeMultiSelectTwoOptionsNotSearchable" stepKey="attribute"/> + <createData entity="productAttributeOption1" stepKey="option1"> + <requiredEntity createDataKey="attribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="option2"> + <requiredEntity createDataKey="attribute"/> + </createData> + <!-- Create product attribute set --> + <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <!-- Filter product attribute set by attribute set name --> + <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> + <actionGroup ref="FilterProductAttributeSetGridByAttributeSetName" stepKey="filterProductAttrSetGridByAttrSetName"> + <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> + </actionGroup> + <!-- Assert created attribute in an unassigned attributes --> + <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassignedAttr"/> + <!-- Assign attribute in the group --> + <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$attribute.attribute_code$$"/> + </actionGroup> + <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttributeInGroup"/> + <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> + <!-- Go to Product Attribute Grid page --> + <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> + <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="$$attribute.attribute_code$$" stepKey="fillAttrCodeField" /> + <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearchBtn" /> + <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="chooseFirstRow" /> + <!-- Change attribute property: Frontend Label --> + <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{productDropDownAttribute.attribute_code}}" stepKey="fillDefaultLabel"/> + <!-- Change attribute property: Use in Search >Yes --> + <scrollToTopOfPage stepKey="scrollToTabs"/> + <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" stepKey="waitTabLoad"/> + <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="Yes" stepKey="seeInSearch"/> + <!-- Change attribute property: Visible In Advanced Search >No --> + <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" stepKey="waitTabLoad2"/> + <selectOption selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" userInput="No" stepKey="dontSeeInAdvancedSearch"/> + <!-- Save the new product attributes --> + <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSave"/> + <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> + <!-- Flash cache --> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!-- Go to store's advanced catalog search page --> + <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> + <dontSeeElement selector="{{StorefrontCatalogSearchAdvancedFormSection.AttributeByCode('$$attribute.attribute_code$$')}}" stepKey="dontSeeAttribute"/> + </test> +</tests> From 5a1ccb84be4afb5cc2a7a09e23b6d7c3cdf61e79 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Fri, 8 Feb 2019 14:00:55 -0600 Subject: [PATCH 061/254] MC-4527: Convert VerifyConfigurableProductEntityPriceTest to MFTF --- ...oductPriceWithDisabledChildProductTest.xml | 156 +++++++++++++++++ ...uctPriceWithOutOfStockChildProductTest.xml | 157 ++++++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml new file mode 100644 index 0000000000000..0d83d59b4ce32 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckConfigurableProductPriceWithDisabledChildProductTest"> + <annotations> + <stories value="Configurable Product"/> + <title value="Check Price for Configurable Product when One Child is Disabled, Others are Enabled"/> + <description value="Login as admin and check the configurable product price when one child product is disabled "/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13749"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <!-- Create Default Category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!-- Create an attribute with three 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> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> + <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> + <!-- Get the third option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> + <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"/> + <field key="price">10.00</field> + </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"/> + <field key="price">20.00</field> + </createData> + <!--Create a simple product and give it the attribute with the Third option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + <field key="price">30.00</field> + </createData> + <!-- Create the configurable product --> + <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </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> + <!-- Add the third simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct3"/> + </createData> + </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="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Open Product in Store Front Page --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront"/> + <waitForPageLoad stepKey="waitForProductToLoad"/> + <!-- Verify category,Configurable product and initial price --> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryInFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductNameInStoreFront"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeInitialPriceInStoreFront"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createConfigProduct.sku$$" stepKey="seeProductSkuInStoreFront"/> + <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStatusInStoreFront"/> + <!-- Verify First Child Product attribute option is displayed --> + <see selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="seeOption1"/> + <!-- Select product Attribute option1, option2 and option3 and verify changes in the price --> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="selectOption1"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeChildProduct1PriceInStoreFront"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="selectOption2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeChildProduct2PriceInStoreFront"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption3.label$$" stepKey="selectOption3"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct3.price$$" stepKey="seeChildProduct3PriceInStoreFront"/> + <!-- Open Product Index Page and Filter First Child product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <argument name="product" value="ApiSimpleOne"/> + </actionGroup> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="selectFirstRow"/> + <waitForPageLoad stepKey="waitForProductPageToLoad"/> + <!-- Disable the product --> + <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + <!-- Open Product Store Front Page --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront1"/> + <waitForPageLoad stepKey="waitForProductToLoad1"/> + <!-- Verify category,configurable product and updated price --> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryInFrontPage1"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductNameInStoreFront1"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeUpdatedProductPriceInStoreFront"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createConfigProduct.sku$$" stepKey="seeProductSkuInStoreFront1"/> + <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStatusInStoreFront1"/> + <!-- Verify product Attribute Option1 is not displayed --> + <dontSee selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="dontSeeOption1"/> + <!--Select product Attribute option2 and option3 and verify changes in the price --> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="selectTheOption2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeSecondChildProductPriceInStoreFront"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption3.label$$" stepKey="selectTheOption3"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct3.price$$" stepKey="seeThirdProductPriceInStoreFront"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest.xml new file mode 100644 index 0000000000000..f5650feeff71b --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest.xml @@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest"> + <annotations> + <stories value="Configurable Product"/> + <title value="Check Price for Configurable Product when Child is Out of Stock"/> + <description value="Login as admin and check the configurable product price when one child product is out of stock "/> + <severity value="CRITICAL"/> + <testCaseId value="MC-13750"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <!-- Create Default Category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <!-- Create an attribute with three 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> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> + <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> + <!-- Get the third option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> + <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"/> + <field key="price">10.00</field> + </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"/> + <field key="price">20.00</field> + </createData> + <!--Create a simple product and give it the attribute with the Third option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + <field key="price">30.00</field> + </createData> + <!-- Create the configurable product --> + <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </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> + <!-- Add the third simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct3"/> + </createData> + </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="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Open Product Store Front Page --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront"/> + <waitForPageLoad stepKey="waitForProductToLoad"/> + <!-- Verify category,Configurable product and initial price --> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryInFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductNameInStoreFront"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeInitialProductPriceInStoreFront"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createConfigProduct.sku$$" stepKey="seeProductSkuInStoreFront"/> + <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStatusInStoreFront"/> + <!-- Verify First Child Product attribute option is displayed --> + <see selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="seeOption1"/> + <!-- Select product Attribute option1, option2 and option3 and verify changes in the price --> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="selectOption1"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeChildProduct1PriceInStoreFront"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="selectOption2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeChildProduct2PriceInStoreFront"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption3.label$$" stepKey="selectOption3"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct3.price$$" stepKey="seeChildProduct3PriceInStoreFront"/> + <!-- Open Product Index Page and Filter First Child product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <argument name="product" value="ApiSimpleOne"/> + </actionGroup> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="selectFirstRow"/> + <waitForPageLoad stepKey="waitForProductPageToLoad"/> + <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrolllToProductQuantity"/> + <!-- Change the product stock status as 'Out Of Stock'--> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="disableProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + <!-- Open Product Store Front Page --> + <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront1"/> + <waitForPageLoad stepKey="waitForProductToLoad1"/> + <!-- Verify category,configurable product details and updated price --> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryInFrontPage1"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductNameInStoreFront1"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeUpdatedProductPriceInStoreFront"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createConfigProduct.sku$$" stepKey="seeProductSkuInStoreFront1"/> + <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStatusInStoreFront1"/> + <!-- Verify product Attribute Option1 is not displayed --> + <dontSee selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="dontSeeOption1"/> + <!-- Select product Attribute option2 and option3 and verify changes in the price --> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="selectTheOption2"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeSecondChildProductPriceInStoreFront"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption3.label$$" stepKey="selectTheOption3"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct3.price$$" stepKey="seeThirdProductPriceInStoreFront"/> + </test> +</tests> From 753c83187dd71bed499dfeaae327b1a919a798f0 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Fri, 8 Feb 2019 14:59:37 -0600 Subject: [PATCH 062/254] MC-10913: Customer Login on Storefront with Incorrect Credentials --- .../Page/StorefrontCustomerSignInPage.xml | 1 + ...StorefrontCustomerLoginMessagesSection.xml | 14 ++++++++ ...frontLoginWithIncorrectCredentialsTest.xml | 35 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontLoginWithIncorrectCredentialsTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml index 0d4fef8f6e967..b4814a3e4bedd 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerSignInPage" url="/customer/account/login/" area="storefront" module="Magento_Customer"> <section name="StorefrontCustomerSignInFormSection" /> + <section name="StorefrontCustomerLoginMessagesSection"/> </page> </pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml new file mode 100644 index 0000000000000..554b27d93afb6 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.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="StorefrontCustomerLoginMessagesSection"> + <element name="errorMessage" type="text" selector=".message-error"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontLoginWithIncorrectCredentialsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontLoginWithIncorrectCredentialsTest.xml new file mode 100644 index 0000000000000..9a6c1c5ec8988 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontLoginWithIncorrectCredentialsTest.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="StorefrontLoginWithIncorrectCredentialsTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Login"/> + <title value="Customer Login on Storefront with Incorrect Credentials"/> + <description value="Customer Login on Storefront with Incorrect Credentials"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-10913"/> + <group value="Customer"/> + <group value="mtf-migrated"/> + </annotations> + <before> + <createData stepKey="customer" entity="Simple_US_Customer"/> + </before> + <after> + <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="$$customer.password$$INVALID" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> + <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> + <see stepKey="seeErrorMessage" selector="{{StorefrontCustomerLoginMessagesSection.errorMessage}}" userInput="The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later."/> + </test> +</tests> From ddde7f678a59c1a0cc9779f290d9195861108cb7 Mon Sep 17 00:00:00 2001 From: Dominic <d.fernando@ism-apac.com> Date: Sun, 10 Feb 2019 11:12:20 +0530 Subject: [PATCH 063/254] fixed-pagination-issue-admin-review-grid --- lib/web/mage/adminhtml/grid.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index c9af869d79161..b16b2c9defce0 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -531,12 +531,19 @@ define([ /** * @param {Object} event */ - inputPage: function (event) { + inputPage: function (event, lastId) { var element = Event.element(event), - keyCode = event.keyCode || event.which; + keyCode = event.keyCode || event.which, + enteredValue = parseInt(element.value), + lastId = parseInt(lastId); + if (keyCode == Event.KEY_RETURN) { //eslint-disable-line eqeqeq - this.setPage(element.value); + if (enteredValue > lastId) { + this.setPage(lastId); + } else { + this.setPage(element.value); + } } /*if(keyCode>47 && keyCode<58){ From c0a98a087b3df9a0023e9a65ca8e710b4494bdf3 Mon Sep 17 00:00:00 2001 From: Dominic <d.fernando@ism-apac.com> Date: Sun, 10 Feb 2019 11:27:50 +0530 Subject: [PATCH 064/254] fixed-pagination-issue-admin-review-grid --- lib/web/mage/adminhtml/grid.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index b16b2c9defce0..b7e696ae8f4bd 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -530,6 +530,7 @@ define([ /** * @param {Object} event + * @param int lastId */ inputPage: function (event, lastId) { var element = Event.element(event), From b6baf8fbe273d5a28e622ca62ab7aefd42a3069e Mon Sep 17 00:00:00 2001 From: Dominic <d.fernando@ism-apac.com> Date: Sun, 10 Feb 2019 11:31:49 +0530 Subject: [PATCH 065/254] fixed-pagination-issue-admin-review-grid-clean-code --- lib/web/mage/adminhtml/grid.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index b7e696ae8f4bd..e27dd9c2a0d26 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -538,12 +538,11 @@ define([ enteredValue = parseInt(element.value), lastId = parseInt(lastId); - if (keyCode == Event.KEY_RETURN) { //eslint-disable-line eqeqeq if (enteredValue > lastId) { this.setPage(lastId); } else { - this.setPage(element.value); + this.setPage(enteredValue); } } From cab38a21087525b72add16910b6eccf7eb7e1b63 Mon Sep 17 00:00:00 2001 From: Vinai Kopp <vinai@netzarbeiter.com> Date: Sun, 3 Feb 2019 17:46:26 +0530 Subject: [PATCH 066/254] magento/magento2#20773: Make autoloader PSR-4 compliant --- .../Code/Generator/AutoloaderTest.php | 85 +++++++++++++++++++ .../Framework/Code/Generator/Autoloader.php | 70 +++++++++++++-- 2 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php new file mode 100644 index 0000000000000..0e1b51b3ae273 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php @@ -0,0 +1,85 @@ +<?php declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Code\Generator; + +use Magento\Framework\Code\Generator; +use Magento\Framework\Logger\Monolog as MagentoMonologLogger; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; +use Psr\Log\LoggerInterface; + +class AutoloaderTest extends TestCase +{ + /** + * This method exists to fix the wrong return type hint on \Magento\Framework\App\ObjectManager::getInstance. + * This way the IDE knows it's dealing with an instance of \Magento\TestFramework\ObjectManager and + * not \Magento\Framework\App\ObjectManager. The former has the method addSharedInstance, the latter does not. + * + * @return ObjectManager|\Magento\Framework\App\ObjectManager + * @SuppressWarnings(PHPMD.StaticAccess) + */ + private function getTestFrameworkObjectManager() + { + return ObjectManager::getInstance(); + } + + /** + * @before + */ + public function setupLoggerTestDouble(): void + { + $loggerTestDouble = $this->createMock(LoggerInterface::class); + $this->getTestFrameworkObjectManager()->addSharedInstance($loggerTestDouble, MagentoMonologLogger::class); + } + + /** + * @after + */ + public function removeLoggerTestDouble(): void + { + $this->getTestFrameworkObjectManager()->removeSharedInstance(MagentoMonologLogger::class); + } + + /** + * @param \RuntimeException $testException + * @return Generator|MockObject + */ + private function createExceptionThrowingGeneratorTestDouble(\RuntimeException $testException) + { + /** @var Generator|MockObject $generatorStub */ + $generatorStub = $this->createMock(Generator::class); + $generatorStub->method('generateClass')->willThrowException($testException); + + return $generatorStub; + } + + public function testLogsExceptionDuringGeneration(): void + { + $exceptionMessage = 'Test exception thrown during generation'; + $testException = new \RuntimeException($exceptionMessage); + + $loggerMock = ObjectManager::getInstance()->get(LoggerInterface::class); + $loggerMock->expects($this->once())->method('debug')->with($exceptionMessage, ['exception' => $testException]); + + $autoloader = new Autoloader($this->createExceptionThrowingGeneratorTestDouble($testException)); + $this->assertNull($autoloader->load(NonExistingClassName::class)); + } + + public function testFiltersDuplicateExceptionMessages(): void + { + $exceptionMessage = 'Test exception thrown during generation'; + $testException = new \RuntimeException($exceptionMessage); + + $loggerMock = ObjectManager::getInstance()->get(LoggerInterface::class); + $loggerMock->expects($this->once())->method('debug')->with($exceptionMessage, ['exception' => $testException]); + + $autoloader = new Autoloader($this->createExceptionThrowingGeneratorTestDouble($testException)); + $autoloader->load(OneNonExistingClassName::class); + $autoloader->load(AnotherNonExistingClassName::class); + } +} diff --git a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php index c214008393609..1afa97729b158 100644 --- a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php +++ b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php @@ -3,37 +3,89 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Framework\Code\Generator; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Code\Generator; +use Psr\Log\LoggerInterface; class Autoloader { /** - * @var \Magento\Framework\Code\Generator + * @var Generator */ protected $_generator; /** - * @param \Magento\Framework\Code\Generator $generator + * Enables guarding against spamming the debug log with duplicate messages, as + * the generation exception will be thrown multiple times within a single request. + * + * @var string + */ + private $lastGenerationErrorMessage; + + /** + * @param Generator $generator */ - public function __construct( - \Magento\Framework\Code\Generator $generator - ) { + public function __construct(Generator $generator) + { $this->_generator = $generator; } /** * Load specified class name and generate it if necessary * + * According to PSR-4 section 2.4 an autoloader MUST NOT throw an exception and SHOULD NOT return a value. + * + * @see https://www.php-fig.org/psr/psr-4/ + * * @param string $className - * @return bool True if class was loaded + * @return void */ public function load($className) { - if (!class_exists($className)) { - return Generator::GENERATION_ERROR != $this->_generator->generateClass($className); + if (! class_exists($className)) { + try { + $this->_generator->generateClass($className); + } catch (\Exception $exception) { + $this->tryToLogExceptionMessageIfNotDuplicate($exception); + } + } + } + + /** + * @param \Exception $exception + */ + private function tryToLogExceptionMessageIfNotDuplicate(\Exception $exception): void + { + if ($this->lastGenerationErrorMessage !== $exception->getMessage()) { + $this->lastGenerationErrorMessage = $exception->getMessage(); + $this->tryToLogException($exception); + } + } + + /** + * Try to capture the exception message. + * + * The Autoloader is instantiated before the ObjectManager, so the LoggerInterface can not be injected. + * The Logger is instantiated in the try/catch block because ObjectManager might still not be initialized. + * In that case the exception message can not be captured. + * + * The debug level is used for logging in case class generation fails for a common class, but a custom + * autoloader is used later in the stack. A more severe log level would fill the logs with messages on production. + * The exception message now can be accessed in developer mode if debug logging is enabled. + * + * @param \Exception $exception + * @return void + */ + private function tryToLogException(\Exception $exception): void + { + try { + $logger = ObjectManager::getInstance()->get(LoggerInterface::class); + $logger->debug($exception->getMessage(), ['exception' => $exception]); + } catch (\Exception $ignoreThisException) { + // Do not take an action here, since the original exception might have been caused by logger } - return true; } } From 9a6b215c7f282423d27a5d1c87eb61ef0d32147f Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Mon, 11 Feb 2019 09:58:25 -0600 Subject: [PATCH 067/254] MC-10913: Customer Login on Storefront with Incorrect Credentials --- .../Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.xml index 2dea52c1b7fcc..bf84d2be43a11 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/LoginOnFrontendFailTest.xml @@ -9,6 +9,7 @@ <testCase name="Magento\Customer\Test\TestCase\LoginOnFrontendFailTest" summary="Check error message with wrong credentials" ticketId="MAGETWO-16883"> <variation name="LoginOnFrontendFailTestVariation1"> <data name="customer/dataset" xsi:type="string">default</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerLoginErrorMessage" /> </variation> </testCase> From 6cd6dc32c474c496c72533df09db8af7d6848923 Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Mon, 11 Feb 2019 10:08:26 -0600 Subject: [PATCH 068/254] MC-13679: Forgot Password on Storefront validates customer email input --- .../Page/StorefrontCustomerSignInPage.xml | 1 + .../Page/StorefrontForgotPasswordPage.xml | 14 +++++++ ...StorefrontCustomerLoginMessagesSection.xml | 15 +++++++ .../StorefrontCustomerSignInFormSection.xml | 1 + .../StorefrontForgotPasswordSection.xml | 16 ++++++++ .../Test/StorefrontForgotPasswordTest.xml | 40 +++++++++++++++++++ .../TestCase/ForgotPasswordOnFrontendTest.xml | 1 + 7 files changed, 88 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/Page/StorefrontForgotPasswordPage.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontForgotPasswordSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml index 0d4fef8f6e967..b4814a3e4bedd 100644 --- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerSignInPage.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> <page name="StorefrontCustomerSignInPage" url="/customer/account/login/" area="storefront" module="Magento_Customer"> <section name="StorefrontCustomerSignInFormSection" /> + <section name="StorefrontCustomerLoginMessagesSection"/> </page> </pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontForgotPasswordPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontForgotPasswordPage.xml new file mode 100644 index 0000000000000..2633a0c760cec --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontForgotPasswordPage.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="StorefrontForgotPasswordPage" url="/customer/account/forgotpassword/" area="storefront" module="Magento_Customer"> + <section name="StorefrontForgotPasswordSection" /> + </page> +</pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.xml new file mode 100644 index 0000000000000..078021db062cc --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerLoginMessagesSection.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="StorefrontCustomerLoginMessagesSection"> + <element name="successMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector=".message-error"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml index 25c07ca9cb3c9..f52b379379ad1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml @@ -12,6 +12,7 @@ <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="link" selector=".action.remind" timeout="10"/> </section> <section name="StorefrontCustomerSignInPopupFormSection"> <element name="errorMessage" type="input" selector="[data-ui-id='checkout-cart-validationmessages-message-error']"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontForgotPasswordSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontForgotPasswordSection.xml new file mode 100644 index 0000000000000..bdae69c425db1 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontForgotPasswordSection.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="StorefrontForgotPasswordSection"> + <element name="pageTitle" type="text" selector=".page-title"/> + <element name="email" type="input" selector="#email_address"/> + <element name="resetMyPasswordButton" type="button" selector=".action.submit.primary" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml new file mode 100644 index 0000000000000..acacc2c063f7c --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.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="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 customer/captcha/enable 0" stepKey="disableCaptcha"/> + <createData stepKey="customer" entity="Simple_US_Customer"/> + </before> + <after> + <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}}"/> + <seeInUrl stepKey="seeInSignInPage" userInput="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/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/ForgotPasswordOnFrontendTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/ForgotPasswordOnFrontendTest.xml index 0d168451c6d32..7bf916157c5dc 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/ForgotPasswordOnFrontendTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/ForgotPasswordOnFrontendTest.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\Customer\Test\TestCase\ForgotPasswordOnFrontendTest" summary="Forgot customer password on frontend" ticketId="MAGETWO-37145"> <variation name="ForgotPasswordOnFrontendTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customer/dataset" xsi:type="string">customer_US</data> <data name="configData" xsi:type="string">captcha_storefront_disable</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerForgotPasswordSuccessMessage" /> From fe8cc058dbc617ab9c5b3e181a599d923e0f7e06 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Mon, 11 Feb 2019 21:05:32 +0200 Subject: [PATCH 069/254] graphQl-292: payment method list --- .../AvailablePaymentMethodsDataProvider.php | 53 +++++++++++++++++++ .../AvailablePaymentMethodsResolver.php | 48 +++++++++++++++++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 6 +++ 3 files changed, 107 insertions(+) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/PaymentMethod/AvailablePaymentMethodsDataProvider.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethodsResolver.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/PaymentMethod/AvailablePaymentMethodsDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/PaymentMethod/AvailablePaymentMethodsDataProvider.php new file mode 100644 index 0000000000000..daa51d8729995 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/PaymentMethod/AvailablePaymentMethodsDataProvider.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart\PaymentMethod; + +use Magento\Checkout\Api\PaymentInformationManagementInterface; +use Magento\Quote\Api\Data\CartInterface; + +/** + * Get array of available payment methods. + */ +class AvailablePaymentMethodsDataProvider +{ + /** + * @var PaymentInformationManagementInterface + */ + private $informationManagement; + + /** + * AvailablePaymentMethodsDataProvider constructor. + * @param PaymentInformationManagementInterface $informationManagement + */ + public function __construct(PaymentInformationManagementInterface $informationManagement) + { + $this->informationManagement = $informationManagement; + } + + /** + * Collect and return information about available payment methods + * + * @param CartInterface $cart + * @return array + */ + public function getPaymentMethods(CartInterface $cart): array + { + $paymentInformation = $this->informationManagement->getPaymentInformation($cart->getId()); + $paymentMethods = $paymentInformation->getPaymentMethods(); + + $paymentMethodsNested = []; + foreach ($paymentMethods as $paymentMethod) { + $paymentMethodsNested[] = [ + 'title' => $paymentMethod->getTitle(), + 'code' => $paymentMethod->getCode() + ]; + } + + return $paymentMethodsNested; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethodsResolver.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethodsResolver.php new file mode 100644 index 0000000000000..17747ec11b25a --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethodsResolver.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\QuoteGraphQl\Model\Cart\PaymentMethod\AvailablePaymentMethodsDataProvider; + +/** + * Get list of active payment methods resolver. + */ +class AvailablePaymentMethodsResolver implements ResolverInterface +{ + /** + * @var AvailablePaymentMethodsDataProvider + */ + private $addressDataProvider; + + /** + * @param AvailablePaymentMethodsDataProvider $addressDataProvider + */ + public function __construct( + AvailablePaymentMethodsDataProvider $addressDataProvider + ) { + $this->addressDataProvider = $addressDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + + $cart = $value['model']; + + return $this->addressDataProvider->getPaymentMethods($cart); + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 4c1101a5f90a8..072810de0bb47 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -98,6 +98,7 @@ type Cart { items: [CartItemInterface] applied_coupon: AppliedCoupon addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartAddresses") + available_payment_methods : [CheckoutPaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethodsResolver") @doc(description: "Available payment methods") } type CartAddress { @@ -141,6 +142,11 @@ type CheckoutShippingMethod { # TODO: Add more complex structure for shipping rates } +type CheckoutPaymentMethod @doc(description: "The type contains list of active payment methods") { + code : String @doc(description: "The payment method code") + title : String @doc(description: "The payment method title.") +} + enum AdressTypeEnum { SHIPPING BILLING From edc8f53815248c1cb23324ee7c85ebb0d2d197eb Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Mon, 11 Feb 2019 14:19:06 -0600 Subject: [PATCH 070/254] MC-10916: Update Customer Password on Storefront, Valid Current Password MC-10917: Update Customer Password on Storefront, Invalid Current Password MC-10918: Update Customer Password on Storefront, Invalid Confirmation Password --- .../LoginToStorefrontActionGroup.xml | 10 +++ ...frontCustomerAccountChangePasswordPage.xml | 14 ++++ ...frontCustomerAccountInformationSection.xml | 6 +- ...omerDashboardAccountInformationSection.xml | 2 + .../StorefrontCustomerMessagesSection.xml | 15 ++++ .../StorefrontUpdateCustomerPasswordTest.xml | 81 +++++++++++++++++++ .../TestCase/ChangeCustomerPasswordTest.xml | 3 + 7 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAccountChangePasswordPage.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerMessagesSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml index 7be36ffbd9bc4..79961b2bfc297 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml @@ -16,4 +16,14 @@ <fillField stepKey="fillPassword" userInput="{{Customer.password}}" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> </actionGroup> + <actionGroup name="LoginToStorefrontWithEmailAndPasswordActionGroup"> + <arguments> + <argument name="email" type="string"/> + <argument name="password" type="string"/> + </arguments> + <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> + <fillField stepKey="fillEmail" userInput="{{email}}" selector="{{StorefrontCustomerSignInFormSection.emailField}}"/> + <fillField stepKey="fillPassword" userInput="{{password}}" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> + <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAccountChangePasswordPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAccountChangePasswordPage.xml new file mode 100644 index 0000000000000..43198297b1731 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerAccountChangePasswordPage.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="StorefrontCustomerAccountChangePasswordPage" url="/customer/account/edit/changepass/1/" area="storefront" module="Magento_Customer"> + <section name="StorefrontCustomerAccountInformationSection"/> + </page> +</pages> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml index 59da4e9279a03..b819a78002c62 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml @@ -14,6 +14,10 @@ <element name="changeEmail" type="checkbox" selector="#change_email"/> <element name="changePassword" type="checkbox" selector="#change_password"/> <element name="testAddedAttributeFiled" type="input" selector="//input[contains(@id,'{{var}}')]" parameterized="true"/> - <element name="saveButton" type="button" selector="#form-validate .action.save.primary"/> + <element name="saveButton" type="button" selector="#form-validate .action.save.primary" timeout="30"/> + <element name="currentPassword" type="input" selector="#current-password"/> + <element name="newPassword" type="input" selector="#password"/> + <element name="confirmNewPassword" type="input" selector="#password-confirmation"/> + <element name="confirmNewPasswordError" type="text" selector="#password-confirmation-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 index 70d1bb6675db5..e888343a5be2a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml @@ -10,6 +10,8 @@ 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="link" selector=".action.edit" timeout="15"/> + <element name="changePassword" type="link" selector=".action.change-password" timeout="15"/> </section> <section name="StorefrontCustomerAddressSection"> <element name="firstName" type="input" selector="#firstname"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerMessagesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerMessagesSection.xml new file mode 100644 index 0000000000000..07d044921c8e5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerMessagesSection.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="StorefrontCustomerMessagesSection"> + <element name="successMessage" type="text" selector=".message-success"/> + <element name="errorMessage" type="text" selector=".message-error"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml new file mode 100644 index 0000000000000..a168a5f380538 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.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="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="LoginToStorefrontWithEmailAndPasswordActionGroup"> + <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/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/ChangeCustomerPasswordTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/ChangeCustomerPasswordTest.xml index 9765b9d9340a7..f94d99b07ec6e 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/ChangeCustomerPasswordTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/ChangeCustomerPasswordTest.xml @@ -9,6 +9,7 @@ <testCase name="Magento\Customer\Test\TestCase\ChangeCustomerPasswordTest" summary="Change Customer Password from My Account" ticketId="MAGETWO-29411"> <variation name="ChangeCustomerPasswordTestVariation1"> <data name="initialCustomer/dataset" xsi:type="string">default</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customer/data/current_password" xsi:type="string">123123^q</data> <data name="customer/data/password" xsi:type="string">123123^a</data> <data name="customer/data/password_confirmation" xsi:type="string">123123^a</data> @@ -17,6 +18,7 @@ </variation> <variation name="ChangeCustomerPasswordTestVariation2"> <data name="initialCustomer/dataset" xsi:type="string">default</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customer/data/current_password" xsi:type="string">123123</data> <data name="customer/data/password" xsi:type="string">123123^a</data> <data name="customer/data/password_confirmation" xsi:type="string">123123^a</data> @@ -24,6 +26,7 @@ </variation> <variation name="ChangeCustomerPasswordTestVariation3"> <data name="initialCustomer/dataset" xsi:type="string">default</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customer/data/current_password" xsi:type="string">123123^q</data> <data name="customer/data/password" xsi:type="string">123123^a</data> <data name="customer/data/password_confirmation" xsi:type="string">123123^d</data> From b7fe54689d2e1486a9efec9cf1455d3c226fbfa2 Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Tue, 12 Feb 2019 11:43:39 -0600 Subject: [PATCH 071/254] MC-4538: Convert RegisterCustomerFrontendEntityTest to MFTF --- .../ActionGroup/DeleteCustomerActionGroup.xml | 17 ++++++ ...SignUpNewUserFromStorefrontActionGroup.xml | 28 ++++++++-- .../AdminEditCustomerInformationSection.xml | 1 + .../AdminEditCustomerNewsletterSection.xml | 14 +++++ .../StorefrontCustomerCreateFormSection.xml | 1 + ...stomerOnStorefrontSignupNewsletterTest.xml | 54 +++++++++++++++++++ ...AdminCreateNewCustomerOnStorefrontTest.xml | 38 +++++++++++++ 7 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml index 4d531214db150..7fb9f906474f7 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml @@ -24,4 +24,21 @@ <click stepKey="clickOnOk" selector="{{CustomersPageSection.ok}}"/> <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="10"/> </actionGroup> + <actionGroup name="DeleteNewCustomerByEmailActionGroup"> + <arguments> + <argument name="email" type="string"/> + </arguments> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> + <waitForPageLoad stepKey="waitForAdminCustomerPageLoad"/> + <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="clickFilterButton"/> + <waitForPageLoad stepKey="waitForClearFilters"/> + <fillField selector="{{AdminCustomerFiltersSection.emailInput}}" userInput="{{CustomerEntityOne.email}}" stepKey="filterEmail"/> + <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="applyFilter"/> + <waitForPageLoad stepKey="waitForApplyFilters"/> + <checkOption selector="{{CustomersPageSection.customerCheckbox(email)}}" stepKey="seeCustomerEmailInGrid"/> + <click selector="{{CustomersPageSection.actions}}" stepKey="clickActionsDropdown"/> + <click selector="{{CustomersPageSection.delete}}" stepKey="clickDelete"/> + <click selector="{{CustomersPageSection.ok}}" stepKey="clickOkConfirmationButton"/> + <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="10"/> + </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 76acf6e865963..323dbc2bd5b5f 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -24,7 +24,29 @@ <see stepKey="seeLastName" userInput="{{Customer.lastname}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" /> <see stepKey="seeEmail" userInput="{{Customer.email}}" selector="{{StorefrontCustomerDashboardAccountInformationSection.ContactInformation}}" /> </actionGroup> - + + <actionGroup name="StorefrontCreateCustomerSignedUpNewsletterActionGroup"> + <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" /> + <see stepKey="seeThankYouMessage" userInput="Thank you for registering with Main Website Store."/> + <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"> <arguments> <argument name="Address"/> @@ -48,6 +70,6 @@ <actionGroup name="SignUpNewCustomerStorefrontActionGroup" extends="SignUpNewUserFromStorefrontActionGroup"> <waitForPageLoad stepKey="waitForRegistered" after="clickCreateAccountButton"/> - <remove keyForRemoval="seeThankYouMessage" after="waitForRegistered"/> + <remove keyForRemoval="seeThankYouMessage"/> </actionGroup> -</actionGroups> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml index f5bbb84eaa593..0c3d89f5782d8 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml @@ -11,5 +11,6 @@ <section name="AdminEditCustomerInformationSection"> <element name="orders" type="button" selector="#tab_orders_content" timeout="30"/> <element name="addresses" type="button" selector="//a[@id='tab_address']" timeout="30"/> + <element name="newsLetter" type="button" selector="//a[@class='admin__page-nav-link' and @id='tab_newsletter_content']" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.xml new file mode 100644 index 0000000000000..51b4b54c5c8b6 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerNewsletterSection.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="AdminEditCustomerNewsletterSection"> + <element name="subscribedToNewsletter" type="checkbox" selector="//div[@class='admin__field-control control']/input[@name='subscription']"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml index ee14ee5c165c5..8881a2a012ce8 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml @@ -12,6 +12,7 @@ <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"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml new file mode 100644 index 0000000000000..081cd684cf2f0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.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="AdminCreateNewCustomerOnStorefrontSignupNewsletterTest"> + <annotations> + <stories value="Create New Customer"/> + <title value="Create New Customer on Storefront, Sign-up Newsletter"/> + <description value="Test log in to Create New Customer and Create New Customer on Storefront, Sign-up Newsletter"/> + <testCaseId value="MC-10914"/> + <severity value="CRITICAL"/> + <group value="customer"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="DeleteNewCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create new customer on storefront and signup news letter--> + <actionGroup ref="StorefrontCreateCustomerSignedUpNewsletterActionGroup" stepKey="createCustomer"> + <argument name="customer" value="CustomerEntityOne" /> + </actionGroup> + + <!--Verify created new customer in grid--> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> + <waitForPageLoad stepKey="waitForNavigateToCustomersPageLoad"/> + <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="clickFilterButton"/> + <fillField userInput="{{CustomerEntityOne.email}}" selector="{{AdminCustomerFiltersSection.emailInput}}" stepKey="filterEmail"/> + <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="clickApplyFilter"/> + <see selector="{{AdminCustomerGridSection.customerGrid}}" userInput="{{CustomerEntityOne.firstname}}" stepKey="seeAssertCustomerFirstNameInGrid"/> + <see selector="{{AdminCustomerGridSection.customerGrid}}" userInput="{{CustomerEntityOne.lastname}}" stepKey="seeAssertCustomerLastNameInGrid"/> + <see selector="{{AdminCustomerGridSection.customerGrid}}" userInput="{{CustomerEntityOne.email}}" stepKey="seeAssertCustomerEmailInGrid"/> + + <!--Verify created new customer is subscribed to newsletter--> + <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickFirstRowEditLink"/> + <waitForPageLoad stepKey="waitForEditLinkLoad"/> + <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickNewsLetter"/> + <waitForPageLoad stepKey="waitForNewsletterTabToOpen"/> + <seeCheckboxIsChecked selector="{{AdminEditCustomerNewsletterSection.subscribedToNewsletter}}" stepKey="seeAssertSubscribedToNewsletterCheckboxIsChecked"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml new file mode 100644 index 0000000000000..11f2e83f43be9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.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="AdminCreateNewCustomerOnStorefrontTest"> + <annotations> + <stories value="Create New Customer"/> + <title value="Create New Customer on Storefront"/> + <description value="Test log in to Create New Customer and Create New Customer on Storefront"/> + <testCaseId value="MC-10915"/> + <severity value="CRITICAL"/> + <group value="customer"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="DeleteNewCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create new customer on storefront--> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="CustomerEntityOne"/> + </actionGroup> + <seeInCurrentUrl url="{{StorefrontCustomerDashboardPage.url}}" stepKey="seeAssertInCurrentUrl"/> + </test> +</tests> \ No newline at end of file From e4b46e7a032380249747722ba4756fc38ccb34dc Mon Sep 17 00:00:00 2001 From: Dominic <d.fernando@ism-apac.com> Date: Tue, 12 Feb 2019 23:32:12 +0530 Subject: [PATCH 072/254] return-cart-id-null --- .../QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php index f4335b262c854..ff0298416cdf8 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php @@ -81,6 +81,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $this->addProductsToCart->execute($cart, $cartItems); $cartData = $this->extractDataFromCart->execute($cart); + $cartData["cart_id"] = $cartHash; return [ 'cart' => $cartData, From 9e0fb4b008e78caa4fc049166067dedfc1a13742 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Tue, 12 Feb 2019 22:06:36 +0200 Subject: [PATCH 073/254] graphQl-239: refactoring, adjusting tests --- .../Resolver/Product/ProductImage/Url.php | 8 +- .../DataProvider/Image/Placeholder.php | 74 +++++-------------- .../DataProvider/Image/Placeholder/Theme.php | 71 ++++++++++++++++++ .../GraphQl/Catalog/ProductImageTest.php | 13 ++++ 4 files changed, 105 insertions(+), 61 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php index 8b6275f2f3e0e..23a8c2d15c09e 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductImage/Url.php @@ -27,20 +27,18 @@ class Url implements ResolverInterface /** * @var PlaceholderProvider */ - private $_placeholderProvider; + private $placeholderProvider; /** - * Url constructor. * @param ImageFactory $productImageFactory * @param PlaceholderProvider $placeholderProvider */ public function __construct( ImageFactory $productImageFactory, PlaceholderProvider $placeholderProvider - ) { $this->productImageFactory = $productImageFactory; - $this->_placeholderProvider = $placeholderProvider; + $this->placeholderProvider = $placeholderProvider; } /** @@ -83,7 +81,7 @@ private function getImageUrl(string $imageType, ?string $imagePath): string ->setBaseFile($imagePath); if ($image->isBaseFilePlaceholder()) { - return $this->_placeholderProvider->getPlaceholder($imageType); + return $this->placeholderProvider->getPlaceholder($imageType); } return $image->getUrl(); 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 d9691a079a328..a558ec2bfa253 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,102 +8,64 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image; use Magento\Catalog\Model\View\Asset\PlaceholderFactory; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\View\Asset\Repository as AssetRepository; -use Magento\Framework\View\Design\Theme\ThemeProviderInterface; -use Magento\Store\Model\StoreManagerInterface; /** - * Class Placeholder - * @package Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image + * Image Placeholder provider */ class Placeholder { /** * @var PlaceholderFactory */ - private $_placeholderFactory; + private $placeholderFactory; + /** * @var AssetRepository */ - private $_assetRepository; - /** - * @var ScopeConfigInterface - */ - private $_scopeConfig; - /** - * @var StoreManagerInterface - */ - private $_storeManager; + private $assetRepository; + /** - * @var ThemeProviderInterface + * @var Theme */ - private $_themeProvider; + private $theme; /** * Placeholder constructor. * @param PlaceholderFactory $placeholderFactory * @param AssetRepository $assetRepository - * @param ScopeConfigInterface $scopeConfig - * @param StoreManagerInterface $storeManager - * @param ThemeProviderInterface $themeProvider + * @param Theme $theme */ public function __construct( PlaceholderFactory $placeholderFactory, AssetRepository $assetRepository, - ScopeConfigInterface $scopeConfig, - StoreManagerInterface $storeManager, - ThemeProviderInterface $themeProvider + Theme $theme ) { - $this->_placeholderFactory = $placeholderFactory; - $this->_assetRepository = $assetRepository; - $this->_scopeConfig = $scopeConfig; - $this->_storeManager = $storeManager; - $this->_themeProvider = $themeProvider; + $this->placeholderFactory = $placeholderFactory; + $this->assetRepository = $assetRepository; + $this->theme = $theme; } /** * Get placeholder * - * @param $imageType + * @param string $imageType * @return string * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function getPlaceholder($imageType): string + public function getPlaceholder(string $imageType): string { - $imageAsset = $this->_placeholderFactory->create(['type' => $imageType]); + $imageAsset = $this->placeholderFactory->create(['type' => $imageType]); // check if placeholder defined in config if ($imageAsset->getFilePath()) { return $imageAsset->getUrl(); } - return $this->_assetRepository->createAsset( + $themeData = $this->theme->getThemeData(); + return $this->assetRepository->createAsset( "Magento_Catalog::images/product/placeholder/{$imageType}.jpg", - $this->getThemeData() + $themeData )->getUrl(); } - - /** - * Get theme model - * - * @return mixed - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - public function getThemeData() - { - $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/Image/Placeholder/Theme.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php new file mode 100644 index 0000000000000..3988dbbc25b33 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/Image/Placeholder/Theme.php @@ -0,0 +1,71 @@ +<?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; + +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/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductImageTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductImageTest.php index bb9c67f6d753d..b957292a3ac28 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductImageTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductImageTest.php @@ -60,12 +60,25 @@ public function testProductWithoutBaseImage() url label } + small_image { + url + label + } } } } QUERY; $response = $this->graphQlQuery($query); self::assertEquals('Simple Product', $response['products']['items'][0]['image']['label']); + self::assertStringEndsWith( + 'images/product/placeholder/image.jpg', + $response['products']['items'][0]['image']['url'] + ); + self::assertEquals('Simple Product', $response['products']['items'][0]['small_image']['label']); + self::assertStringEndsWith( + 'images/product/placeholder/small_image.jpg', + $response['products']['items'][0]['small_image']['url'] + ); } /** From 74e632e5c9ffcca9d90efa8691e59d88429d3f3d Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Tue, 12 Feb 2019 14:38:11 -0600 Subject: [PATCH 074/254] MC-4416: Convert CreateProductAttributeEntityTest to MFTF - Fixed TestCaseId for variation. --- .../Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml index 551d7af1d6a30..f8f0d066f7a4e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml @@ -179,7 +179,7 @@ <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-10894"/> + <testCaseId value="MC-10896"/> <group value="Catalog"/> </annotations> <before> From e72d452e33ec3519594380dae052a353d6d9c1d6 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Tue, 12 Feb 2019 23:39:57 +0200 Subject: [PATCH 075/254] graphQl-239: fixed namespace --- .../Model/Resolver/Products/DataProvider/Image/Placeholder.php | 1 + .../Resolver/Products/DataProvider/Image/Placeholder/Theme.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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 a558ec2bfa253..699613893949b 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 @@ -7,6 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image\Placeholder\Theme; use Magento\Catalog\Model\View\Asset\PlaceholderFactory; use Magento\Framework\View\Asset\Repository as AssetRepository; 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 index 3988dbbc25b33..dc48c5ef69346 100644 --- 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 @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image; +namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image\Placeholder; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\View\Design\Theme\ThemeProviderInterface; From b46c740c6f2a2ccc940f4040b45314b0301d644b Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 13 Feb 2019 09:01:48 -0600 Subject: [PATCH 076/254] MAGETWO-98225: AuthorizenetAcceptjs broken for multi-shipping checkout - Fixed option - Added config test coverage --- app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml | 2 +- .../Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml b/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml index b8292839c3bd1..b9f8d40b03006 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml +++ b/app/code/Magento/AuthorizenetAcceptjs/etc/payment.xml @@ -9,7 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Payment:etc/payment.xsd"> <methods> <method name="authorizenet_acceptjs"> - <allow_multiple_address>1</allow_multiple_address> + <allow_multiple_address>0</allow_multiple_address> </method> </methods> </payment> diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php index 7e6aeda5a7a6d..a37f927274242 100644 --- a/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetAcceptjs/Gateway/ConfigTest.php @@ -8,6 +8,7 @@ namespace Magento\AuthorizenetAcceptjs\Gateway; +use Magento\Framework\Config\Data; use Magento\Payment\Model\Method\Adapter; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; @@ -40,5 +41,11 @@ public function testVerifyConfiguration() $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); } } From b3c1ecd401bad12cdf6d9c8e5a064297e4bc5837 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Wed, 13 Feb 2019 09:04:53 -0600 Subject: [PATCH 077/254] MC-4544: Convert PasswordAutocompleteOffTest to MFTF --- .../LoginToStorefrontActionGroup.xml | 8 +++ .../StorefrontCustomerActionGroup.xml | 3 + .../Section/StorefrontPanelHeaderSection.xml | 1 + .../Mftf/Test/PasswordAutocompleteOffTest.xml | 62 +++++++++++++++++++ .../TestCase/PasswordAutocompleteOffTest.xml | 1 + 5 files changed, 75 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml index 7be36ffbd9bc4..ae720e9bbf2a1 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml @@ -16,4 +16,12 @@ <fillField stepKey="fillPassword" userInput="{{Customer.password}}" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> </actionGroup> + <actionGroup name="StoreFrontPasswordAutoCompleteOffActionGroup"> + <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> + <assertElementContainsAttribute selector="{{StorefrontCustomerSignInFormSection.passwordField}}" attribute="autocomplete" expectedValue="off" stepKey="assertSignInPasswordAutocompleteOff"/> + </actionGroup> + <actionGroup name="AuthorizationPopUpPasswordAutoCompleteOffActionGroup"> + <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.password}}" stepKey="waitPasswordFieldVisible"/> + <assertElementContainsAttribute selector="{{StorefrontCustomerSignInPopupFormSection.password}}" attribute="autocomplete" expectedValue="off" stepKey="assertAuthorizationPopupPasswordAutocompleteOff"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml index fc5c1b881752e..1759c8a4eb6a3 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml @@ -15,4 +15,7 @@ stepKey="clickHeaderCustomerMenuButton" /> <click selector="{{StorefrontPanelHeaderSection.customerLogoutLink}}" stepKey="clickSignOutButton" /> </actionGroup> + <actionGroup name="CustomerSignInStorefrontActionGroup"> + <click stepKey="signIn" selector="{{StorefrontPanelHeaderSection.customerLoginLink}}" /> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml index 1955c6a417ba9..0e6970e350240 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml @@ -14,6 +14,7 @@ <element name="notYouLink" type="button" selector=".greet.welcome span a"/> <element name="customerWelcome" type="text" selector=".panel.header .customer-welcome"/> <element name="customerWelcomeMenu" type="text" selector=".panel.header .customer-welcome .customer-menu"/> + <element name="customerLoginLink" type="text" selector=".panel.header .header.links .authorization-link a" timeout="30"/> <element name="customerLogoutLink" type="text" selector=".panel.header .customer-welcome .customer-menu .authorization-link a" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml new file mode 100644 index 0000000000000..798f9476e16a7 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.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="PasswordAutocompleteOffTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Password Autocomplete"/> + <title value="[Security] Autocomplete attribute with off value is added to password input"/> + <description value="[Security] Autocomplete attribute with off value is added to password input"/> + <testCaseId value="MC-13678"/> + <severity value="CRITICAL"/> + <group value="customers"/> + <group value="banana"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Configure Magento via CLI: disable_guest_checkout --> + <magentoCLI command="config:set checkout/options/guest_checkout 0" stepKey="disableGuestCheckout"/> + + <!-- Configure Magento via CLI: password_autocomplete_off--> + <magentoCLI command="config:set customer/password/autocomplete_on_storefront 0" stepKey="turnPasswordAutocompleteOff"/> + + <!-- Create a simple product --> + <createData entity="SimpleSubCategory" stepKey="category"/> + <createData entity="SimpleProduct" stepKey="product"> + <requiredEntity createDataKey="category"/> + </createData> + </before> + <after> + <!-- Set Magento configuration back to default values --> + <magentoCLI command="config:set checkout/options/guest_checkout 1" stepKey="disableGuestCheckoutRollback"/> + + <!-- Delete the simple product created in the before block --> + <deleteData createDataKey="product" stepKey="deleteProduct"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + + </after> + + <!-- Go to the created product page and add it to the cart--> + <actionGroup ref="AddSimpleProductToCart" stepKey="cartAddSimpleProductToCart"> + <argument name="product" value="$$product$$"/> + </actionGroup> + + <!--Click Sign in - on the top right of the page --> + <actionGroup ref="CustomerSignInStorefrontActionGroup" stepKey="customerSignInStorefront"/> + + <!--Verify if the password field on store front sign-in page has the autocomplete attribute set to off --> + <actionGroup ref="StoreFrontPasswordAutoCompleteOffActionGroup" stepKey="checkStoreFrontPasswordAutoCompleteOff"/> + + <!--Proceed to checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + + <!--Verify if the password field on the authorization popup has the autocomplete attribute set to off --> + <actionGroup ref="AuthorizationPopUpPasswordAutoCompleteOffActionGroup" stepKey="checkAuthorizationPopUpPasswordAutoCompleteOff"/> + </test> +</tests> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.xml index b4188ebd98ae7..7f267c1f171f6 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/PasswordAutocompleteOffTest.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\Customer\Test\TestCase\PasswordAutocompleteOffTest" summary="Test that autocomplete is off" ticketId="MAGETWO-45324"> <variation name="RegisterCustomerFrontendEntityTestVariation1" summary="Test that autocomplete is off"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product/dataset" xsi:type="string">default</data> <data name="configData" xsi:type="string">disable_guest_checkout,password_autocomplete_off</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerPasswordAutocompleteOnAuthorizationPopup" /> From 85ed36805bcc51c85155c1d9e7f27722607325f4 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Wed, 13 Feb 2019 13:17:57 -0600 Subject: [PATCH 078/254] MC-4535: Convert DeleteCustomerBackendEntityTest to MFTF --- .../AdminDeleteCustomerActionGroup.xml | 18 +++++++ .../Mftf/Section/AdminCustomerGridSection.xml | 1 + .../Mftf/Test/AdminDeleteCustomerTest.xml | 47 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml index d08f10b22419d..a3334dbd6c842 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml @@ -21,4 +21,22 @@ <click stepKey="accept" selector="{{AdminCustomerGridMainActionsSection.ok}}"/> <see stepKey="seeSuccessMessage" userInput="were deleted."/> </actionGroup> + <actionGroup name="DeleteCustomerByEmailActionGroup"> + <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"/> + <click selector="{{CustomersPageSection.ok}}" stepKey="clickOkConfirmationButton"/> + <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="10"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml index d9d3bfe7f737c..7cc32a5fcecd7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml @@ -11,5 +11,6 @@ <section name="AdminCustomerGridSection"> <element name="customerGrid" type="text" selector="table[data-role='grid']"/> <element name="firstRowEditLink" type="text" selector="tr[data-repeat-index='0'] .action-menu-item" timeout="30"/> + <element name="selectFirstRow" type="checkbox" selector="//td[@class='data-grid-checkbox-cell']"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.xml new file mode 100644 index 0000000000000..7fef916fc458a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminDeleteCustomerTest.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="AdminDeleteCustomerTest"> + <annotations> + <stories value="Delete customer"/> + <title value="DeleteCustomerBackendEntityTestVariation1"/> + <description value="Login as admin and delete the customer"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14587"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <!-- Create Customer --> + <createData entity="CustomerEntityOne" stepKey="createCustomer"/> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Delete created customer --> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteCustomer"> + <argument name="email" value="$$createCustomer.email$$"/> + </actionGroup> + <seeElement selector="{{CustomersPageSection.deletedSuccessMessage}}" stepKey="seeSuccessMessage"/> + <waitForPageLoad stepKey="waitForCustomerGridPageToLoad"/> + + <!--Assert Customer is not in Grid --> + <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="clickFilterButton"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="cleanFiltersIfTheySet"/> + <waitForPageLoad stepKey="waitForClearFilters1"/> + <fillField selector="{{AdminCustomerFiltersSection.emailInput}}" userInput="$$createCustomer.email$$" stepKey="filterEmail"/> + <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="applyFilter"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <see selector="{{AdminCustomerGridSection.customerGrid}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> + </test> +</tests> From 55374cc319ee981d710c869e4fd387097e44651d Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Wed, 13 Feb 2019 13:21:36 -0600 Subject: [PATCH 079/254] MC-4544: Convert PasswordAutocompleteOffTest to MFTF removed a group tag that wasn't necessary --- .../Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml index 798f9476e16a7..7e5def00f813b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml @@ -16,7 +16,6 @@ <testCaseId value="MC-13678"/> <severity value="CRITICAL"/> <group value="customers"/> - <group value="banana"/> <group value="mtf_migrated"/> </annotations> <before> From 8200acc3a86d55016b2d26e502e05ddac98e300b Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Wed, 13 Feb 2019 16:14:26 -0600 Subject: [PATCH 080/254] MC-4526: Convert VerifyConfigurableProductLayeredNavigationTest to MFTF --- .../StorefrontProductAttributeActionGroup.xml | 26 +++ ...nfigurableProductLayeredNavigationTest.xml | 148 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductAttributeActionGroup.xml create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductAttributeActionGroup.xml new file mode 100644 index 0000000000000..8fab65527315e --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductAttributeActionGroup.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 attribute options on the category page --> + <actionGroup name="SelectStorefrontSideBarAttributeOption"> + <arguments> + <argument name="categoryName" type="string"/> + <argument name="attributeDefaultLabel" type="string"/> + </arguments> + <amOnPage url="categoryName" stepKey="openCategoryStoreFrontPage"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(categoryName)}}" stepKey="seeCategoryInFrontPage"/> + <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(categoryName)}}" stepKey="clickOnCategory"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad1"/> + <seeElement selector="{{StorefrontCategorySidebarSection.filterOptionsTitle(attributeDefaultLabel)}}" stepKey="seeAttributeOptionsTitle"/> + <click selector="{{StorefrontCategorySidebarSection.filterOptionsTitle(attributeDefaultLabel)}}" stepKey="clickAttributeOptions"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml new file mode 100644 index 0000000000000..a230b6851da73 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontVerifyConfigurableProductLayeredNavigationTest"> + <annotations> + <stories value="Create configurable product"/> + <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"/> + <group value="ConfigurableProduct"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + + <!-- Create Default Category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + + <!-- Create an attribute with three options --> + <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="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> + <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> + + <!-- Get the third option of the attribute created --> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> + <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 a simple product and give it the attribute with the Third option --> + <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct3"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </createData> + + <!-- Create the configurable product --> + <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + <requiredEntity createDataKey="getConfigAttributeOption3"/> + </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> + + <!-- Add the third simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct3"/> + </createData> + </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="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Open Product Index Page and Filter First Child product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> + <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> + <argument name="product" value="ApiSimpleOne"/> + </actionGroup> + + <!-- Change the First Child Product stock status as 'Out Of Stock'--> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="selectFirstRow"/> + <waitForPageLoad stepKey="waitForProductPageToLoad"/> + <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToProductQuantity"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="disableProduct"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + + <!--Open Category in Store Front and select product attribute option from sidebar --> + <actionGroup ref="SelectStorefrontSideBarAttributeOption" stepKey="selectStorefrontProductAttributeOption"> + <argument name="categoryName" value="$$createCategory.name$$"/> + <argument name="attributeDefaultLabel" value="$$createConfigProductAttribute.default_value$$"/> + </actionGroup> + + <!--Assert Out Of Stock product is not visible in Storefront Page --> + <dontSee selector="{{StorefrontCategorySidebarSection.filterOption}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="dontSeeOption1"/> + <see selector="{{StorefrontCategorySidebarSection.filterOption}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="seeOption2"/> + <see selector="{{StorefrontCategorySidebarSection.filterOption}}" userInput="$$getConfigAttributeOption3.label$$" stepKey="seeOption3"/> + </test> +</tests> \ No newline at end of file From 04f325eb0455bb1e80c82a0a4d150936c3d2c6b5 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 14 Feb 2019 10:59:15 -0600 Subject: [PATCH 081/254] MC-4379: Convert SubcategoryNotIncludeInNavigationMenuTest to MFTF - Reference createData instead of entity directly --- ...nuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml | 6 +++--- ...tegoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml | 6 +++--- ...nuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml | 6 +++--- ...dminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml index d6126916046a9..fd22142fcb097 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveAndNotIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml @@ -35,7 +35,7 @@ <!--Create subcategory under parent category --> <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> <waitForPageLoad stepKey="waitForCategoryToLoad"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatInactiveNotInMenu.name)}}" stepKey="selectCategory"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="selectCategory"/> <waitForPageLoad stepKey="waitForPageToLoad"/> <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> <checkOption selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="enableCategory"/> @@ -45,9 +45,9 @@ <waitForPageLoad stepKey="waitForSecondCategoryToSave"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> <!-- Verify Parent Category and Sub category is not visible in navigation menu --> - <amOnPage url="{{CatInactiveNotInMenu.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> + <amOnPage url="$$createCategory.name_lwr$$/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> <waitForPageLoad stepKey="waitForCategoryStoreFrontPageToLoad"/> - <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(CatInactiveNotInMenu.name)}}" stepKey="dontSeeCategoryOnStoreNavigationBar"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="dontSeeCategoryOnStoreNavigationBar"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSubCategoryOnStoreNavigation"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml index 9362719ba56de..b6c76d6577210 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveCategoryAndSubcategoryIsNotVisibleInNavigationMenuTest.xml @@ -34,7 +34,7 @@ <!--Create subcategory under parent category --> <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> <waitForPageLoad stepKey="waitForCategoryToLoad"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatNotActive.name)}}" stepKey="selectCategory"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="selectCategory"/> <waitForPageLoad stepKey="waitForPageToLoad"/> <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> <checkOption selector="{{AdminCategoryBasicFieldSection.EnableCategory}}" stepKey="enableCategory"/> @@ -44,9 +44,9 @@ <waitForPageLoad stepKey="waitForSecondCategoryToSave"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> <!-- Verify Parent Category and Sub category is not visible in navigation menu --> - <amOnPage url="{{CatNotActive.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> + <amOnPage url="$$createCategory.name_lwr$$/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> <waitForPageLoad stepKey="waitForCategoryStoreFrontPageToLoad"/> - <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(CatNotActive.name)}}" stepKey="dontSeeCategoryOnStoreNavigationBar"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="dontSeeCategoryOnStoreNavigationBar"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSubCategoryOnStoreNavigation"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml index 2b8f47d1ca265..c9cd9acd9708c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckInactiveIncludeInMenuCategoryAndSubcategoryIsNotVisibleInNavigationTest.xml @@ -35,7 +35,7 @@ <!--Create subcategory under parent category --> <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> <waitForPageLoad stepKey="waitForCategoryToLoad"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(CatNotIncludeInMenu.name)}}" stepKey="selectCategory"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="selectCategory"/> <waitForPageLoad stepKey="waitForPageToLoad"/> <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="addSubCategoryName"/> @@ -45,9 +45,9 @@ <waitForPageLoad stepKey="waitForSecondCategoryToSave"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> <!-- Verify Parent Category and Sub category is not visible in navigation menu --> - <amOnPage url="{{CatNotIncludeInMenu.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> + <amOnPage url="$$createCategory.name_lwr$$/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> <waitForPageLoad stepKey="waitForCategoryStoreFrontPageToLoad"/> - <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(CatNotIncludeInMenu.name)}}" stepKey="dontSeeCategoryOnStoreNavigationBar"/> + <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="dontSeeCategoryOnStoreNavigationBar"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSubCategoryOnStoreNavigation"/> </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 e250fa7b15dd1..f5872ac3efca0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckSubCategoryIsNotVisibleInNavigationMenuTest.xml @@ -34,7 +34,7 @@ <!--Create subcategory under parent category --> <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> <waitForPageLoad stepKey="waitForCategoryToLoad"/> - <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(_defaultCategory.name)}}" stepKey="selectCategory"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="selectCategory"/> <waitForPageLoad stepKey="waitForPageToLoad"/> <click selector="{{AdminCategorySidebarActionSection.AddSubcategoryButton}}" stepKey="clickOnAddSubCategoryButton"/> <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="{{SimpleSubCategory.name}}" stepKey="addSubCategoryName"/> @@ -44,9 +44,9 @@ <waitForPageLoad stepKey="waitForSecondCategoryToSave"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="seeSuccessMessage"/> <!-- Verify Parent Category is visible in navigation menu and Sub category is not visible in navigation menu --> - <amOnPage url="{{_defaultCategory.name_lwr}}/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> + <amOnPage url="$$createCategory.name_lwr$$/{{SimpleSubCategory.name_lwr}}.html" stepKey="openCategoryStoreFrontPage"/> <waitForPageLoad stepKey="waitForCategoryStoreFrontPageToLoad"/> - <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(_defaultCategory.name)}}" stepKey="seeCategoryOnStoreNavigationBar"/> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryOnStoreNavigationBar"/> <dontSeeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(SimpleSubCategory.name)}}" stepKey="dontSeeSubCategoryOnStoreNavigation"/> </test> </tests> From 285e95b8e89acfe781be241b2e668eb9a7d0d4dd Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Thu, 14 Feb 2019 11:30:48 -0600 Subject: [PATCH 082/254] MC-4544: Convert PasswordAutocompleteOffTest to MFTF Merge branch 'mtf-eol' of https://github.com/magento-pangolin/magento2ce into MC-4544 Updated as per review comments - removed old code --- .../Mftf/ActionGroup/LoginToStorefrontActionGroup.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml index ae720e9bbf2a1..7be36ffbd9bc4 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml @@ -16,12 +16,4 @@ <fillField stepKey="fillPassword" userInput="{{Customer.password}}" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> </actionGroup> - <actionGroup name="StoreFrontPasswordAutoCompleteOffActionGroup"> - <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> - <assertElementContainsAttribute selector="{{StorefrontCustomerSignInFormSection.passwordField}}" attribute="autocomplete" expectedValue="off" stepKey="assertSignInPasswordAutocompleteOff"/> - </actionGroup> - <actionGroup name="AuthorizationPopUpPasswordAutoCompleteOffActionGroup"> - <waitForElementVisible selector="{{StorefrontCustomerSignInPopupFormSection.password}}" stepKey="waitPasswordFieldVisible"/> - <assertElementContainsAttribute selector="{{StorefrontCustomerSignInPopupFormSection.password}}" attribute="autocomplete" expectedValue="off" stepKey="assertAuthorizationPopupPasswordAutocompleteOff"/> - </actionGroup> </actionGroups> From 098ca6c5877c4ea1c4e6ab9ed815a04e07a03d64 Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Thu, 14 Feb 2019 15:29:12 -0600 Subject: [PATCH 083/254] MC-4534: Convert UpdateCustomerFrontendEntityTest to MFTF --- .../ActionGroup/DeleteCustomerActionGroup.xml | 21 ++++- ...SignUpNewUserFromStorefrontActionGroup.xml | 77 ++++++++++++++++++- .../Customer/Test/Mftf/Data/AddressData.xml | 48 +++++++++++- .../Customer/Test/Mftf/Data/CustomerData.xml | 13 +++- .../Mftf/Section/AdminCustomerGridSection.xml | 3 +- .../StorefrontCustomerSidebarSection.xml | 4 +- ...refrontUpdateCustomerAddressFranceTest.xml | 52 +++++++++++++ .../StorefrontUpdateCustomerAddressUKTest.xml | 57 ++++++++++++++ ...pdateCustomerInformationAddAddressTest.xml | 57 ++++++++++++++ 9 files changed, 323 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml index 4d531214db150..04568eb6eb23f 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml @@ -24,4 +24,23 @@ <click stepKey="clickOnOk" selector="{{CustomersPageSection.ok}}"/> <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="10"/> </actionGroup> -</actionGroups> + <actionGroup name="DeleteCustomerByEmailActionGroup"> + <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="10"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml index 76acf6e865963..f59374605608d 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -39,15 +39,86 @@ <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}}"/> - <selectOption stepKey="selectCounty" selector="{{StorefrontCustomerAddressSection.country}}" userInput="{{Address.country_id}}"/> - <click stepKey="saveAddress" selector="{{StorefrontCustomerAddressSection.saveAddress}}"/> </actionGroup> + <actionGroup name="VerifyCustomerBillingAddress"> + <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"> + <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"> + <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"> + <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"> + <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"> <waitForPageLoad stepKey="waitForRegistered" after="clickCreateAccountButton"/> - <remove keyForRemoval="seeThankYouMessage" after="waitForRegistered"/> + <remove keyForRemoval="seeThankYouMessage"/> </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 da36cf722325e..e456ddbec5d4d 100755 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -191,4 +191,50 @@ <data key="default_billing">true</data> <data key="default_shipping">false</data> </entity> -</entities> + <entity name="updateCustomerUKAddress" type="address"> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="company">Magento</data> + <data key="telephone">0123456789-02134567</data> + <array key="street"> + <item>172, Westminster Bridge Rd</item> + <item>7700 xyz street</item> + </array> + <data key="country_id">GB</data> + <data key="country">United Kingdom</data> + <data key="city">London</data> + <!-- State not required for UK address on frontend--> + <data key="state"> </data> + <data key="postcode">12345</data> + </entity> + <entity name="updateCustomerFranceAddress" type="address"> + <data key="firstname">Jaen</data> + <data key="lastname">Reno</data> + <data key="company">Magento</data> + <data key="telephone">555-888-111-999</data> + <array key="street"> + <item>18-20 Rue Maréchal Lecler</item> + <item>18-20 Rue Maréchal Lecler</item> + </array> + <data key="country_id">FR</data> + <data key="country">France</data> + <data key="city">Quintin</data> + <data key="state">Côtes-d'Armor</data> + <data key="postcode">12345</data> + </entity> + <entity name="updateCustomerNoXSSInjection" type="address"> + <data key="firstname">Jany</data> + <data key="lastname">Doe</data> + <data key="company">Magento</data> + <data key="telephone">555-888-111-999</data> + <array key="street"> + <item>7700 West Parmer Lane</item> + <item>7700 West Parmer Lane</item> + </array> + <data key="country_id">US</data> + <data key="country">United States</data> + <data key="city">Denver</data> + <data key="state">Colorado</data> + <data key="postcode">12345</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index b3c0d8d9e0047..e3bcef319262e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -178,4 +178,15 @@ <requiredEntity type="address">US_Default_Billing_Address_TX</requiredEntity> <requiredEntity type="address">US_Default_Shipping_Address_CA</requiredEntity> </entity> -</entities> + <entity name="Colorado_US_Customer" 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">Patric.Patric@example.com</data> + <data key="firstname">Patrick</title></head><svg/onload=alert('XSS')></data> + <data key="lastname"><script>alert('Last name')</script></data> + <data key="password">123123^q</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml index d9d3bfe7f737c..a49a545bbe10a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml @@ -11,5 +11,6 @@ <section name="AdminCustomerGridSection"> <element name="customerGrid" type="text" selector="table[data-role='grid']"/> <element name="firstRowEditLink" type="text" selector="tr[data-repeat-index='0'] .action-menu-item" timeout="30"/> + <element name="selectFirstRow" type="checkbox" selector="//td[@class='data-grid-checkbox-cell']"/> </section> -</sections> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml index 0e31f0e0c7782..bed0c71ae7fad 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSidebarSection.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontCustomerSidebarSection"> <element name="sidebarTab" type="text" selector="//div[@id='block-collapsible-nav']//a[text()='{{var1}}']" parameterized="true"/> - <element name="sidebarCurrentTab" type="text" selector="//div[@id='block-collapsible-nav']//strong[contains(text(), '{{var}}')]" parameterized="true"/> + <element name="sidebarCurrentTab" type="text" selector="//div[@id='block-collapsible-nav']//*[contains(text(), '{{var}}')]" parameterized="true"/> </section> -</sections> +</sections> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.xml new file mode 100644 index 0000000000000..dae456c96a679 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressFranceTest.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="StorefrontUpdateCustomerAddressFranceTest"> + <annotations> + <stories value="Update Customer Address"/> + <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"/> + <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> + </before> + <after> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Update customer address France in storefront--> + <actionGroup ref="EnterCustomerAddressInfo" 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"> + <argument name="address" value="updateCustomerFranceAddress"/> + </actionGroup> + + <!--Verify customer default shipping address--> + <actionGroup ref="VerifyCustomerShippingAddressWithState" stepKey="verifyShippingAddress"> + <argument name="address" value="updateCustomerFranceAddress"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.xml new file mode 100644 index 0000000000000..7b6e695aa8dc4 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressUKTest.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="StorefrontUpdateCustomerAddressUKTest"> + <annotations> + <stories value="Update Customer Address"/> + <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"/> + <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> + </before> + <after> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <argument name="email" value="{{CustomerEntityOne.email}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Update customer address UK in storefront--> + <actionGroup ref="EnterCustomerAddressInfo" 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"> + <argument name="address" value="updateCustomerUKAddress"/> + </actionGroup> + + <!--Verify customer default shipping address--> + <actionGroup ref="VerifyCustomerShippingAddress" stepKey="verifyShippingAddress"> + <argument name="address" value="updateCustomerUKAddress"/> + </actionGroup> + + <!--Verify customer name on frontend--> + <actionGroup ref="VerifyCustomerNameOnFrontend" stepKey="verifyVerifyCustomerName"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.xml new file mode 100644 index 0000000000000..e11404db9a9a9 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest.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="StorefrontVerifyNoXssInjectionOnUpdateCustomerInformationAddAddressTest"> + <annotations> + <stories value="Update Customer Address"/> + <title value="[Security] Verify No XSS Injection on Update Customer Information Add Address"/> + <description value="Test log in to Storefront and Verify No XSS Injection on Update Customer Information Add Address"/> + <testCaseId value="MC-10910"/> + <severity value="CRITICAL"/> + <group value="customer"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> + <argument name="Customer" value="Colorado_US_Customer"/> + </actionGroup> + </before> + <after> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <argument name="email" value="{{Colorado_US_Customer.email}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Update customer address in storefront--> + <actionGroup ref="EnterCustomerAddressInfo" 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"> + <argument name="address" value="updateCustomerNoXSSInjection"/> + </actionGroup> + + <!--Verify customer default shipping address--> + <actionGroup ref="VerifyCustomerShippingAddressWithState" stepKey="verifyShippingAddress"> + <argument name="address" value="updateCustomerNoXSSInjection"/> + </actionGroup> + + <!--Verify customer name on frontend--> + <actionGroup ref="VerifyCustomerNameOnFrontend" stepKey="verifyVerifyCustomerName"> + <argument name="customer" value="Colorado_US_Customer"/> + </actionGroup> + </test> +</tests> \ No newline at end of file From 7e17bf2ba5e5cd8716bd27f37bc8c86bf3c8c656 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 15 Feb 2019 09:47:29 +0200 Subject: [PATCH 084/254] MAGETWO-98087: [2.3] Customer Group Regression issues from MAGETWO-96886 --- .../Magento/Backend/Model/Session/Quote.php | 2 +- .../Adminhtml/Order/Create/Form/Account.php | 11 ++- .../Order/Create/Form/AccountTest.php | 83 ++++++++++++++----- 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/Backend/Model/Session/Quote.php b/app/code/Magento/Backend/Model/Session/Quote.php index 11edaa26f443f..b0bdf29441225 100644 --- a/app/code/Magento/Backend/Model/Session/Quote.php +++ b/app/code/Magento/Backend/Model/Session/Quote.php @@ -149,7 +149,7 @@ public function getQuote() $this->_quote = $this->quoteFactory->create(); if ($this->getStoreId()) { if (!$this->getQuoteId()) { - $this->_quote->setCustomerGroupId($this->groupManagement->getDefaultGroup()->getId()); + $this->_quote->setCustomerGroupId($this->groupManagement->getDefaultGroup($this->getStoreId())->getId()); $this->_quote->setIsActive(false); $this->_quote->setStoreId($this->getStoreId()); 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 f53fe4b4f745a..ccfa21f7b25cd 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 @@ -9,7 +9,6 @@ use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Data\Form\Element\AbstractElement; use Magento\Framework\Pricing\PriceCurrencyInterface; -use Magento\Store\Model\ScopeInterface; /** * Create order account form @@ -133,8 +132,14 @@ protected function _prepareForm() $this->_addAttributesToForm($attributes, $fieldset); $this->_form->addFieldNameSuffix('order[account]'); - $storeId = (int)$this->_sessionQuote->getStoreId(); - $this->_form->setValues($this->extractValuesFromAttributes($attributes, $storeId)); + $formValues = $this->getFormValues(); + foreach ($attributes as $code => $attribute) { + $defaultValue = $attribute->getDefaultValue(); + if (isset($defaultValue) && !isset($formValues[$code])) { + $formValues[$code] = $defaultValue; + } + } + $this->_form->setValues($formValues); return $this; } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php index b289e9b94558e..b3cae0b71237e 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php @@ -5,7 +5,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Sales\Block\Adminhtml\Order\Create\Form; @@ -23,7 +22,10 @@ use PHPUnit\Framework\MockObject\MockObject; /** + * Class for test Account + * * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AccountTest extends \PHPUnit\Framework\TestCase { @@ -43,23 +45,33 @@ class AccountTest extends \PHPUnit\Framework\TestCase private $session; /** - * @magentoDataFixture Magento/Sales/_files/quote.php + * {@inheritdoc} */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $quote = $this->objectManager->create(Quote::class)->load(1); + parent::setUp(); + } + + /** + * Test for get form with existing customer + * + * @magentoDataFixture Magento/Customer/_files/customer.php + */ + public function testGetFormWithCustomer() + { + $customerGroup = 2; + $quote = $this->objectManager->create(Quote::class); $this->session = $this->getMockBuilder(SessionQuote::class) ->disableOriginalConstructor() - ->setMethods(['getCustomerId', 'getStore', 'getStoreId', 'getQuote', 'getQuoteId']) + ->setMethods(['getCustomerId','getQuote']) ->getMock(); - $this->session->method('getCustomerId') - ->willReturn(1); $this->session->method('getQuote') ->willReturn($quote); - $this->session->method('getQuoteId') - ->willReturn($quote->getId()); + $this->session->method('getCustomerId') + ->willReturn(1); + /** @var LayoutInterface $layout */ $layout = $this->objectManager->get(LayoutInterface::class); $this->accountBlock = $layout->createBlock( @@ -67,18 +79,20 @@ protected function setUp() 'address_block' . rand(), ['sessionQuote' => $this->session] ); - parent::setUp(); - } - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testGetForm() - { + $fixtureCustomerId = 1; + /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ + $customerRepository = $this->objectManager->get(\Magento\Customer\Api\CustomerRepositoryInterface::class); + /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + $customer = $customerRepository->getById($fixtureCustomerId); + $customer->setGroupId($customerGroup); + $customerRepository->save($customer); + $expectedFields = ['group_id', 'email']; $form = $this->accountBlock->getForm(); self::assertEquals(1, $form->getElements()->count(), "Form has invalid number of fieldsets"); $fieldset = $form->getElements()[0]; + $content = $form->toHtml(); self::assertEquals(count($expectedFields), $fieldset->getElements()->count()); @@ -88,22 +102,45 @@ public function testGetForm() sprintf('Unexpected field "%s" in form.', $element->getId()) ); } + + self::assertContains( + '<option value="'.$customerGroup.'" selected="selected">Wholesale</option>', + $content, + 'The Customer Group specified for the chosen customer should be selected.' + ); + + self::assertContains( + 'value="'.$customer->getEmail().'"', + $content, + 'The Customer Email specified for the chosen customer should be input ' + ); } /** * Tests a case when user defined custom attribute has default value. * - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoConfigFixture current_store customer/create_account/default_group 3 + * @magentoDataFixture Magento/Store/_files/core_second_third_fixturestore.php + * @magentoConfigFixture current_store customer/create_account/default_group 2 + * @magentoConfigFixture secondstore_store customer/create_account/default_group 3 */ public function testGetFormWithUserDefinedAttribute() { + /** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ + $storeManager = Bootstrap::getObjectManager()->get(\Magento\Store\Model\StoreManagerInterface::class); + $secondStore = $storeManager->getStore('secondstore'); + + $quoteSession = $this->objectManager->get(SessionQuote::class); + $quoteSession->setStoreId($secondStore->getId()); + $formFactory = $this->getFormFactoryMock(); $this->objectManager->addSharedInstance($formFactory, FormFactory::class); /** @var LayoutInterface $layout */ $layout = $this->objectManager->get(LayoutInterface::class); - $accountBlock = $layout->createBlock(Account::class, 'address_block' . rand()); + $accountBlock = $layout->createBlock( + Account::class, + 'address_block' . rand() + ); $form = $accountBlock->getForm(); $form->setUseContainer(true); @@ -116,7 +153,7 @@ public function testGetFormWithUserDefinedAttribute() ); self::assertContains( - '<option value="3" selected="selected">Customer Group 1</option>', + '<option value="3" selected="selected">Retailer</option>', $content, 'The Customer Group specified for the chosen store should be selected.' ); @@ -162,13 +199,13 @@ private function createCustomerGroupAttribute(): AttributeMetadataInterface { /** @var Option $option1 */ $option1 = $this->objectManager->create(Option::class); - $option1->setValue(3); - $option1->setLabel('Customer Group 1'); + $option1->setValue(2); + $option1->setLabel('Wholesale'); /** @var Option $option2 */ $option2 = $this->objectManager->create(Option::class); - $option2->setValue(4); - $option2->setLabel('Customer Group 2'); + $option2->setValue(3); + $option2->setLabel('Retailer'); /** @var AttributeMetadataInterfaceFactory $attributeMetadataFactory */ $attributeMetadataFactory = $this->objectManager->create(AttributeMetadataInterfaceFactory::class); From 2474891bd7927a41825d6505797647bdb843673c Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Fri, 15 Feb 2019 10:31:19 +0200 Subject: [PATCH 085/254] MAGETWO-98013: [2.3] Configurable product is displayed as In Stock in product grid when it's set to out of stock --- ...uantityAndStockStatusFieldToCollection.php | 32 +++++ app/code/Magento/Catalog/etc/adminhtml/di.xml | 1 + .../Product/QuantityAndStockStatusTest.php | 119 ++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 app/code/Magento/Catalog/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php new file mode 100644 index 0000000000000..cafe175a27e13 --- /dev/null +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Ui\DataProvider\Product; + +use Magento\Framework\Data\Collection; +use Magento\Ui\DataProvider\AddFieldToCollectionInterface; + +/** + * Class AddQuantityAndStockStatusFieldToCollection + */ +class AddQuantityAndStockStatusFieldToCollection implements AddFieldToCollectionInterface +{ + /** + * @inheritdoc + */ + public function addField(Collection $collection, $field, $alias = null) + { + $collection->joinField( + 'quantity_and_stock_status', + 'cataloginventory_stock_item', + 'is_in_stock', + 'product_id=entity_id', + '{{table}}.stock_id=1', + 'left' + ); + } +} diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index c04cfb2dce00a..c2028b921efb8 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -87,6 +87,7 @@ <arguments> <argument name="addFieldStrategies" xsi:type="array"> <item name="websites" xsi:type="object">Magento\Catalog\Ui\DataProvider\Product\AddWebsitesFieldToCollection</item> + <item name="quantity_and_stock_status" xsi:type="object">Magento\Catalog\Ui\DataProvider\Product\AddQuantityAndStockStatusFieldToCollection</item> </argument> <argument name="addFilterStrategies" xsi:type="array"> <item name="store_id" xsi:type="object">Magento\Catalog\Ui\DataProvider\Product\AddStoreFieldToCollection</item> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php new file mode 100644 index 0000000000000..150c3f64e6c1c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.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\Ui\DataProvider\Product; + +use Magento\Eav\Setup\EavSetup; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Model\Product; +use Magento\CatalogInventory\Model\Stock\StockItemRepository; +use Magento\CatalogInventory\Api\StockItemCriteriaInterface; +use Magento\Catalog\Api\Data\EavAttributeInterface; + +/** + * Class QuantityAndStockStatusTest + */ +class QuantityAndStockStatusTest extends TestCase +{ + /** + * @var string + */ + private static $quantityAndStockStatus = 'quantity_and_stock_status'; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + /** + * @var int + */ + private $isUsedInGridValue; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $eavSetup = $this->objectManager->create(EavSetup::class); + $this->isUsedInGridValue = $eavSetup->getAttribute( + Product::ENTITY, + self::$quantityAndStockStatus, + EavAttributeInterface::IS_USED_IN_GRID + ); + $eavSetup->addAttribute( + Product::ENTITY, + self::$quantityAndStockStatus, + [ + EavAttributeInterface::IS_USED_IN_GRID => 1, + ] + ); + } + + /** + * Test product stock status in the products grid column + * + * @magentoDataFixture Magento/Checkout/_files/simple_product.php + */ + public function testProductStockStatus() + { + $stockItemRepository = $this->objectManager->create(StockItemRepository::class); + + /** @var StockItemCriteriaInterface $stockItemCriteria */ + $stockItemCriteria = $this->objectManager->create(StockItemCriteriaInterface::class); + + $savedStockItem = current($stockItemRepository->getList($stockItemCriteria) + ->getItems()); + $savedStockItemId = $savedStockItem->getItemId(); + + $savedStockItem->setIsInStock(true); + $savedStockItem->save(); + + $savedStockItem->setIsInStock(false); + $savedStockItem->save(); + + $dataProvider = $this->objectManager->create( + ProductDataProvider::class, + [ + 'name' => 'product_listing_data_source', + 'primaryFieldName' => 'entity_id', + 'requestFieldName' => 'id', + 'addFieldStrategies' => [ + 'quantity_and_stock_status' => + $this->objectManager->get(AddQuantityAndStockStatusFieldToCollection::class) + ] + ] + ); + + $dataProvider->addField(self::$quantityAndStockStatus); + $data = $dataProvider->getData(); + + $this->assertEquals( + $data['items'][0][self::$quantityAndStockStatus], + $savedStockItem->load($savedStockItemId) + ->getData('is_in_stock') + ); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $eavSetup = $this->objectManager->create(EavSetup::class); + $eavSetup->addAttribute( + Product::ENTITY, + self::$quantityAndStockStatus, + [ + EavAttributeInterface::IS_USED_IN_GRID => $this->isUsedInGridValue, + ] + ); + parent::tearDown(); + } +} From 548dd31502ccda30a2d5b48e2b73b527bb033145 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 15 Feb 2019 11:17:36 +0200 Subject: [PATCH 086/254] MAGETWO-98087: [2.3] Customer Group Regression issues from MAGETWO-96886 --- .../Adminhtml/Order/Create/Form/Account.php | 38 ------------------- 1 file changed, 38 deletions(-) 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 ccfa21f7b25cd..b2dcf949ef2d2 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 @@ -192,42 +192,4 @@ public function getFormValues() return $data; } - - /** - * Extract the form values from attributes. - * - * @param array $attributes - * @param int $storeId - * @return array - */ - private function extractValuesFromAttributes(array $attributes, int $storeId): array - { - $formValues = $this->getFormValues(); - foreach ($attributes as $code => $attribute) { - $defaultValue = $attribute->getDefaultValue(); - if (isset($defaultValue) && !isset($formValues[$code])) { - $formValues[$code] = $defaultValue; - } - if ($code === 'group_id' && empty($formValues[$code])) { - $formValues[$code] = $this->getDefaultCustomerGroup($storeId); - } - } - - return $formValues; - } - - /** - * Gets default customer group. - * - * @param int $storeId - * @return string|null - */ - private function getDefaultCustomerGroup(int $storeId): ?string - { - return $this->_scopeConfig->getValue( - 'customer/create_account/default_group', - ScopeInterface::SCOPE_STORE, - $storeId - ); - } } From 7001229a0efc0cd916a89b56cee3f17b0774196b Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 15 Feb 2019 13:00:55 +0200 Subject: [PATCH 087/254] MAGETWO-98087: [2.3] Customer Group Regression issues from MAGETWO-96886 --- app/code/Magento/Backend/Model/Session/Quote.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Backend/Model/Session/Quote.php b/app/code/Magento/Backend/Model/Session/Quote.php index b0bdf29441225..e32f1bc57596e 100644 --- a/app/code/Magento/Backend/Model/Session/Quote.php +++ b/app/code/Magento/Backend/Model/Session/Quote.php @@ -149,7 +149,8 @@ public function getQuote() $this->_quote = $this->quoteFactory->create(); if ($this->getStoreId()) { if (!$this->getQuoteId()) { - $this->_quote->setCustomerGroupId($this->groupManagement->getDefaultGroup($this->getStoreId())->getId()); + $customerGroupId = $this->groupManagement->getDefaultGroup($this->getStoreId())->getId(); + $this->_quote->setCustomerGroupId($customerGroupId); $this->_quote->setIsActive(false); $this->_quote->setStoreId($this->getStoreId()); From 58e282ad53ec18af2a5c33d2822696b144ac5486 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Fri, 15 Feb 2019 14:23:30 +0200 Subject: [PATCH 088/254] MAGETWO-98087: [2.3] Customer Group Regression issues from MAGETWO-96886 --- .../Test/Unit/Model/Session/QuoteTest.php | 5 +++- .../Adminhtml/Order/Create/Form/Account.php | 28 ++++++++++++++----- .../Order/Create/Form/AccountTest.php | 2 +- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php b/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php index 869d4ba3f45b1..d159225089afc 100644 --- a/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php +++ b/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php @@ -267,7 +267,10 @@ public function testGetQuoteWithoutQuoteId() $cartInterfaceMock->expects($this->atLeastOnce())->method('getId')->willReturn($quoteId); $defaultGroup = $this->getMockBuilder(\Magento\Customer\Api\Data\GroupInterface::class)->getMock(); $defaultGroup->expects($this->any())->method('getId')->will($this->returnValue($customerGroupId)); - $this->groupManagementMock->expects($this->any())->method('getDefaultGroup')->willReturn($defaultGroup); + $this->groupManagementMock + ->method('getDefaultGroup') + ->with($storeId) + ->willReturn($defaultGroup); $dataCustomerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->disableOriginalConstructor() 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 b2dcf949ef2d2..c2a0eadf7b67a 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 @@ -132,13 +132,8 @@ protected function _prepareForm() $this->_addAttributesToForm($attributes, $fieldset); $this->_form->addFieldNameSuffix('order[account]'); - $formValues = $this->getFormValues(); - foreach ($attributes as $code => $attribute) { - $defaultValue = $attribute->getDefaultValue(); - if (isset($defaultValue) && !isset($formValues[$code])) { - $formValues[$code] = $defaultValue; - } - } + + $formValues = $this->extractValuesFromAttributes($attributes); $this->_form->setValues($formValues); return $this; @@ -192,4 +187,23 @@ public function getFormValues() return $data; } + + /** + * Extract the form values from attributes. + * + * @param array $attributes + * @return array + */ + private function extractValuesFromAttributes(array $attributes): array + { + $formValues = $this->getFormValues(); + foreach ($attributes as $code => $attribute) { + $defaultValue = $attribute->getDefaultValue(); + if (isset($defaultValue) && !isset($formValues[$code])) { + $formValues[$code] = $defaultValue; + } + } + + return $formValues; + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php index b3cae0b71237e..b75501911be6b 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AccountTest.php @@ -45,7 +45,7 @@ class AccountTest extends \PHPUnit\Framework\TestCase private $session; /** - * {@inheritdoc} + * @inheritdoc */ protected function setUp() { From 5821d6d01956b65e82d08923abb846cf4965ba2f Mon Sep 17 00:00:00 2001 From: John S <john00ivy@gmail.com> Date: Fri, 15 Feb 2019 09:56:03 -0600 Subject: [PATCH 089/254] MC-4416: Convert CreateProductAttributeEntityTest to MFTF - Editing selectors slightly to make them more stable. - Adding Logout AG to the "after" block. --- .../ActionGroup/AdminProductAttributeActionGroup.xml | 9 +++++++++ .../Section/AdminCreateProductAttributeSection.xml | 4 ++-- .../Section/AdminProductAddAttributeModalSection.xml | 6 +++--- .../Mftf/Test/CreateProductAttributeEntityTest.xml | 12 +++++++++++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 89321f8f6506a..c6dc796df54b3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -19,6 +19,7 @@ <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="clickOnAttributeRow"/> <waitForPageLoad stepKey="waitForPageLoad2" /> </actionGroup> + <actionGroup name="navigateToEditProductAttribute"> <arguments> <argument name="ProductAttribute" type="string"/> @@ -30,6 +31,7 @@ <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="navigateToAttributeEditPage3" /> <waitForPageLoad stepKey="waitForPageLoad3" /> </actionGroup> + <actionGroup name="changeUseForPromoRuleConditionsProductAttribute"> <arguments> <argument name="option" type="string"/> @@ -40,6 +42,7 @@ <click selector="{{AttributePropertiesSection.Save}}" stepKey="saveAttribute"/> <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product attribute." stepKey="successMessage"/> </actionGroup> + <actionGroup name="deleteProductAttribute" extends="navigateToCreatedProductAttribute"> <click selector="{{AttributePropertiesSection.DeleteAttribute}}" stepKey="deleteAttribute"/> <click selector="{{ModalConfirmationSection.OkButton}}" stepKey="ClickOnDeleteButton"/> @@ -47,6 +50,7 @@ <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="waitForSuccessMessage"/> </actionGroup> + <!--Clicks Add Attribute and adds the given attribute--> <actionGroup name="addProductAttributeInProductModal"> <arguments> @@ -61,6 +65,7 @@ <checkOption selector="{{AdminProductAddAttributeModalSection.firstRowCheckBox}}" stepKey="checkAttribute"/> <click stepKey="addSelected" selector="{{AdminProductAddAttributeModalSection.addSelected}}"/> </actionGroup> + <!--Clicks createNewAttribute and fills out form--> <actionGroup name="createProductAttribute"> <arguments> @@ -72,12 +77,14 @@ <selectOption selector="{{AttributePropertiesSection.ValueRequired}}" stepKey="checkRequired" userInput="{{attribute.is_required_admin}}"/> <click stepKey="saveAttribute" selector="{{AttributePropertiesSection.Save}}"/> </actionGroup> + <!-- Inputs text default value and attribute code--> <actionGroup name="createProductAttributeWithTextField" extends="createProductAttribute" insertAfter="checkRequired"> <click stepKey="openAdvancedProperties" selector="{{AdvancedAttributePropertiesSection.AdvancedAttributePropertiesSectionToggle}}"/> <fillField stepKey="fillCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attribute.attribute_code}}"/> <fillField stepKey="fillDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueText}}" userInput="{{attribute.default_value}}"/> </actionGroup> + <!-- Inputs date default value and attribute code--> <actionGroup name="createProductAttributeWithDateField" extends="createProductAttribute" insertAfter="checkRequired"> <arguments> @@ -87,6 +94,7 @@ <fillField stepKey="fillCode" selector="{{AdvancedAttributePropertiesSection.AttributeCode}}" userInput="{{attribute.attribute_code}}"/> <fillField stepKey="fillDefaultValue" selector="{{AdvancedAttributePropertiesSection.DefaultValueDate}}" userInput="{{date}}"/> </actionGroup> + <!-- Creates dropdown option at row without saving--> <actionGroup name="createAttributeDropdownNthOption"> <arguments> @@ -99,6 +107,7 @@ <fillField stepKey="fillAdmin" selector="{{AttributePropertiesSection.dropdownNthOptionAdmin(row)}}" userInput="{{adminName}}"/> <fillField stepKey="fillStoreView" selector="{{AttributePropertiesSection.dropdownNthOptionDefaultStoreView(row)}}" userInput="{{frontName}}"/> </actionGroup> + <!-- Creates dropdown option at row as default--> <actionGroup name="createAttributeDropdownNthOptionAsDefault" extends="createAttributeDropdownNthOption"> <checkOption selector="{{AttributePropertiesSection.dropdownNthOptionIsDefault(row)}}" stepKey="setAsDefault" after="fillStoreView"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index 5f083171f97cb..6d7eb574b04d2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -31,8 +31,8 @@ <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 button.action-accept"/> - <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <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"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAddAttributeModalSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAddAttributeModalSection.xml index 7962e8117a8be..a3c98e43b4510 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAddAttributeModalSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAddAttributeModalSection.xml @@ -9,11 +9,11 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAddAttributeModalSection"> - <element name="addSelected" type="button" selector=".product_form_product_form_add_attribute_modal .page-main-actions button.action-primary" timeout="30"/> + <element name="addSelected" type="button" selector=".product_form_product_form_add_attribute_modal .page-main-actions .action-primary" timeout="30"/> <element name="filters" type="button" selector=".product_form_product_form_add_attribute_modal button[data-action='grid-filter-expand']" timeout="30"/> <element name="attributeCodeFilter" type="textarea" selector=".product_form_product_form_add_attribute_modal input[name='attribute_code']"/> - <element name="clearFilters" type="button" selector=".product_form_product_form_add_attribute_modal button.action-clear" timeout="30"/> + <element name="clearFilters" type="button" selector=".product_form_product_form_add_attribute_modal .action-clear" timeout="30"/> <element name="firstRowCheckBox" type="input" selector=".product_form_product_form_add_attribute_modal .data-grid-checkbox-cell input"/> - <element name="applyFilters" type="button" selector=".product_form_product_form_add_attribute_modal .admin__data-grid-filters-footer button.action-secondary" timeout="30"/> + <element name="applyFilters" type="button" selector=".product_form_product_form_add_attribute_modal .admin__data-grid-filters-footer .action-secondary" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml index f8f0d066f7a4e..2290f5de0069d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml @@ -18,6 +18,7 @@ <testCaseId value="MC-10894"/> <group value="Catalog"/> </annotations> + <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> @@ -28,6 +29,7 @@ <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.--> @@ -74,6 +76,7 @@ <issueId value="MC-13817"/> </skip> </annotations> + <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> @@ -84,6 +87,7 @@ <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 --> @@ -131,6 +135,7 @@ <testCaseId value="MC-10897"/> <group value="Catalog"/> </annotations> + <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> @@ -141,6 +146,7 @@ <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.--> @@ -182,6 +188,7 @@ <testCaseId value="MC-10896"/> <group value="Catalog"/> </annotations> + <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> @@ -192,6 +199,7 @@ <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.--> @@ -266,6 +274,7 @@ <testCaseId value="MC-10898"/> <group value="Catalog"/> </annotations> + <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> @@ -276,6 +285,7 @@ <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.--> @@ -311,7 +321,6 @@ <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="addProductAttributeInProductModal" stepKey="addAttributeToProduct"> @@ -343,6 +352,7 @@ <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.--> From 111dd2ceafbfc7aae5d8e6bd553117c3936515bb Mon Sep 17 00:00:00 2001 From: Dave Macaulay <macaulay@adobe.com> Date: Fri, 15 Feb 2019 17:13:56 +0100 Subject: [PATCH 090/254] MC-14858: Failing MFTF Test: CheckMessageDisplayedForUsedCompanyTest - Ensure filters are cleared after executing test --- .../Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml index 7c24a8aba27bd..79eec02a828f6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryWithProductsGridFilter.xml @@ -28,6 +28,7 @@ <actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProduct2"> <argument name="product" value="SimpleProduct"/> </actionGroup> + <actionGroup ref="NavigateToAndResetProductGridToDefaultView" stepKey="NavigateToAndResetProductGridToDefaultView"/> <actionGroup ref="logout" stepKey="logout"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductList"/> From d128b959538f952eedafda0fdfbad5c5479fe620 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Fri, 15 Feb 2019 10:15:43 -0600 Subject: [PATCH 091/254] MC-4544: Convert PasswordAutocompleteOffTest to MFTF Rectified StoreFront to Storefront - all files --- ...oreFrontPasswordAutocompleteOffActionGroup.xml | 2 +- ...orefrontPasswordAutocompleteOffActionGroup.xml | 14 ++++++++++++++ .../StoreFrontClickSignInButtonActionGroup.xml | 4 ++-- .../StorefrontClickSignInButtonActionGroup.xml | 15 +++++++++++++++ .../Mftf/Test/PasswordAutocompleteOffTest.xml | 5 +++-- 5 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickSignInButtonActionGroup.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml index b4c32132070d7..23a067cd94eea 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStoreFrontPasswordAutocompleteOffActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertStoreFrontPasswordAutoCompleteOffActionGroup"> + <actionGroup name="AssertStorefrontPasswordAutoCompleteOffActionGroup"> <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> <assertElementContainsAttribute selector="{{StorefrontCustomerSignInFormSection.passwordField}}" attribute="autocomplete" expectedValue="off" stepKey="assertSignInPasswordAutocompleteOff"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.xml new file mode 100644 index 0000000000000..23a067cd94eea --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontPasswordAutocompleteOffActionGroup.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="AssertStorefrontPasswordAutoCompleteOffActionGroup"> + <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> + <assertElementContainsAttribute selector="{{StorefrontCustomerSignInFormSection.passwordField}}" attribute="autocomplete" expectedValue="off" stepKey="assertSignInPasswordAutocompleteOff"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml index 01b678732caab..b12858fc1037e 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StoreFrontClickSignInButtonActionGroup.xml @@ -8,8 +8,8 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StoreFrontClickSignInButtonActionGroup"> - <waitForPageLoad stepKey="waitForStoreFrontSignInPageLoad"/> + <actionGroup name="StorefrontClickSignInButtonActionGroup"> <click stepKey="signIn" selector="{{StorefrontPanelHeaderSection.customerLoginLink}}" /> + <waitForPageLoad stepKey="waitForStorefrontSignInPageLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickSignInButtonActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickSignInButtonActionGroup.xml new file mode 100644 index 0000000000000..b12858fc1037e --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontClickSignInButtonActionGroup.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="StorefrontClickSignInButtonActionGroup"> + <click stepKey="signIn" selector="{{StorefrontPanelHeaderSection.customerLoginLink}}" /> + <waitForPageLoad stepKey="waitForStorefrontSignInPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml index 86485ca8e877f..1cb62b5492334 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <group value="customers"/> <group value="mtf_migrated"/> + <group value="banana"/> </annotations> <before> <!-- Configure Magento via CLI: disable_guest_checkout --> @@ -47,10 +48,10 @@ </actionGroup> <!--Click Sign in - on the top right of the page --> - <actionGroup ref="StoreFrontClickSignInButtonActionGroup" stepKey="storeFrontClickSignInButton"/> + <actionGroup ref="StorefrontClickSignInButtonActionGroup" stepKey="storeFrontClickSignInButton"/> <!--Verify if the password field on store front sign-in page has the autocomplete attribute set to off --> - <actionGroup ref="AssertStoreFrontPasswordAutoCompleteOffActionGroup" stepKey="assertStoreFrontPasswordAutoCompleteOff"/> + <actionGroup ref="AssertStorefrontPasswordAutoCompleteOffActionGroup" stepKey="assertStorefrontPasswordAutoCompleteOff"/> <!--Proceed to checkout--> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> From 21f2b5cf44a8d0f501f60c66de171180190b02e9 Mon Sep 17 00:00:00 2001 From: Soumya Unnikrishnan <sunnikri@adobe.com> Date: Fri, 15 Feb 2019 11:12:12 -0600 Subject: [PATCH 092/254] MC-4544: Convert PasswordAutocompleteOffTest to MFTF Removed group tag --- .../Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml index 1cb62b5492334..f364d24806b9c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/PasswordAutocompleteOffTest.xml @@ -17,7 +17,6 @@ <severity value="CRITICAL"/> <group value="customers"/> <group value="mtf_migrated"/> - <group value="banana"/> </annotations> <before> <!-- Configure Magento via CLI: disable_guest_checkout --> From 581434ebbfd3aea5e8b6dddf7c19524297db0a19 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Fri, 15 Feb 2019 11:48:04 -0600 Subject: [PATCH 093/254] MC-4410: Convert DeleteAssignedToTemplateProductAttributeTest to MFTF - Restore change to newProductAttribute data entity that I missed during merge --- .../Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml index f0b473c67695c..d51e9441f71ef 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductAttributeData.xml @@ -138,7 +138,7 @@ </entity> <entity name="newProductAttribute" type="ProductAttribute"> <data key="attribute_code" unique="suffix">attribute</data> - <data key="frontend_input">Text Field</data> + <data key="frontend_input">text</data> <data key="scope">global</data> <data key="is_required">false</data> <data key="is_unique">false</data> From 7d74789741c361e5e6ef9663629d42c6be3abdac Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Fri, 15 Feb 2019 13:39:52 -0600 Subject: [PATCH 094/254] MC-4897: Convert CreateCategoryRewriteEntityTest to MFTF --- .../ActionGroup/AdminCategoryActionGroup.xml | 12 +++++ .../AdminUrlRewriteActionGroup.xml | 36 +++++++++++++ .../AdminUrlRewriteGridActionGroup.xml | 28 ++++++++++ ...torefrontUrlRewriteRedirectActionGroup.xml | 21 ++++++++ .../Mftf/Page/AdminUrlRewriteEditPage.xml | 14 +++++ .../Section/AdminUrlRewriteEditSection.xml | 26 ++++++++++ .../Section/AdminUrlRewriteIndexSection.xml | 1 + ...CategoryUrlRewriteAndAddNoRedirectTest.xml | 52 +++++++++++++++++++ ...yUrlRewriteAndAddPermanentRedirectTest.xml | 52 +++++++++++++++++++ ...yUrlRewriteAndAddTemporaryRedirectTest.xml | 52 +++++++++++++++++++ 10 files changed, 294 insertions(+) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 57f91b78fcbe9..1f355e32d7577 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -263,4 +263,16 @@ <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessage"/> </actionGroup> + <actionGroup name="OpenCategoryFromCategoryTree"> + <arguments> + <argument name="category" type="string"/> + </arguments> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> + <waitForPageLoad stepKey="waitForCategoryToLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(category)}}" stepKey="selectCategory"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <waitForElementVisible selector="{{AdminCategoryContentSection.categoryPageTitle}}" stepKey="waitForCategoryTitle"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..6fe473b9a4de8 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml @@ -0,0 +1,36 @@ +<?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="AdminAddUrlRewrite"> + <arguments> + <argument name="category" type="string"/> + <argument name="customUrlRewriteValue" type="string"/> + <argument name="storeValue" type="string"/> + <argument name="requestPath" type="string"/> + <argument name="redirectTypeValue" type="string"/> + <argument name="description" type="string"/> + </arguments> + <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.name$$)}}" 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="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml new file mode 100644 index 0000000000000..4cff40befbcb0 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.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="AdminSearchByRequestPath"> + <arguments> + <argument name="redirectPath" type="string"/> + <argument name="redirectType" type="string"/> + <argument name="targetPath" type="string"/> + </arguments> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> + <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{redirectPath}}" stepKey="fillRedirectPathFilter"/> + <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <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" /> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml new file mode 100644 index 0000000000000..1e0ebbe3e9a44 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.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="StorefrontUrlRewriteRedirect"> + <arguments> + <argument name="category" type="string"/> + <argument name="newRequestPath" type="string"/> + </arguments> + <amOnPage url="{{newRequestPath}}" stepKey="openCategoryInStorefront"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(category)}}" stepKey="seeCategoryOnStoreNavigationBar"/> + <seeElement selector="{{StorefrontCategoryMainSection.CategoryTitle(category)}}" stepKey="seeCategoryInTitle"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml new file mode 100644 index 0000000000000..0007102d6525c --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.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="AdminUrlRewriteEditPage" url="admin/url_rewrite/edit/id" area="admin" module="Magento_UrlRewrite"> + <section name="AdminUrlRewriteEditSection"/> + </page> +</pages> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml new file mode 100644 index 0000000000000..233731247f502 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.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="AdminUrlRewriteEditSection"> + <element name="createCustomUrlRewrite" type="select" selector="//select[@id='entity-type-selector']" /> + <element name="createCustomUrlRewriteValue" type="text" selector="//select[@id='entity-type-selector']/option[contains(.,'{{var}}')]" parameterized="true"/> + <element name="store" type="select" selector="//select[@id='store_id']"/> + <element name="storeValue" type="select" selector="//select[@id='store_id']//option[contains(., '{{var}}')]" parameterized="true" /> + <element name="requestPath" type="input" selector="//input[@id='request_path']"/> + <element name="targetPath" type="input" selector="//input[@id='target_path']"/> + <element name="redirectType" type="select" selector="//select[@id='redirect_type']"/> + <element name="redirectTypeValue" type="select" selector="//select[@id='redirect_type']//option[contains(., '{{Var}}')]" parameterized="true"/> + <element name="description" type="input" selector="#description"/> + <element name="categoryInTree" type="text" selector="//li[contains(@class,'active-category jstree-open')]/a[contains(., '{{var}}')]" parameterized="true"/> + <element name="saveButton" type="button" selector="#save" timeout="30"/> + <element name="deleteButton" type="button" selector="#delete" timeout="30"/> + <element name="okButton" type="button" selector="//button[@class='action-primary action-accept']" timeout="30"/> + </section> +</sections> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml index 7c21acdf943ba..4207dffa4fd43 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml @@ -19,5 +19,6 @@ <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"/> </section> </sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.xml new file mode 100644 index 0000000000000..a7a7c0c73d826 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddNoRedirectTest.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="AdminCreateCategoryUrlRewriteAndAddNoRedirectTest"> + <annotations> + <stories value="Create category URL rewrite"/> + <title value="Create category URL rewrite, with no redirect"/> + <description value="Login as admin and create category UrlRewrite with No redirect"/> + <testCaseId value="MC-5335"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <createData entity="_defaultCategory" stepKey="category"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteRootCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Open Url Rewrite Index Page and update the Custom Url Rewrite, Store, Request Path, Redirect Type and Description --> + <actionGroup ref="AdminAddUrlRewrite" 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="newrequestpath.html"/> + <argument name="redirectTypeValue" value="No"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!-- Get Category ID --> + <actionGroup ref="OpenCategoryFromCategoryTree" stepKey="getCategoryId"> + <argument name="category" value="$$category.name$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> + + <!-- Assert Redirect path, Target Path and Redirect type in grid --> + <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <argument name="redirectPath" value="newrequestpath.html" /> + <argument name="redirectType" value="No" /> + <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.xml new file mode 100644 index 0000000000000..ab423e37378d3 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest.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="AdminCreateCategoryUrlRewriteAndAddPermanentRedirectTest"> + <annotations> + <stories value="Create category URL rewrite"/> + <title value="Create category URL rewrite, add permanent redirect for category"/> + <description value="Login as admin and create category UrlRewrite with Permanent redirect"/> + <testCaseId value="MC-5334"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <createData entity="_defaultCategory" stepKey="category"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteRootCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Open Url Rewrite Index Page and update the Custom Url Rewrite, Store, Request Path, Redirect Type and Description --> + <actionGroup ref="AdminAddUrlRewrite" 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="newrequestpath.html"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!-- Assert Redirect path, Target Path and Redirect type in grid --> + <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <argument name="redirectPath" value="newrequestpath.html" /> + <argument name="redirectType" value="Permanent (301)" /> + <argument name="targetPath" value="$$category.name_lwr$$.html"/> + </actionGroup> + + <!--Assert Updated path directs to the category storefront --> + <actionGroup ref="StorefrontUrlRewriteRedirect" stepKey="openStorefrontUrlRedirectPath"> + <argument name="category" value="$$category.name$$"/> + <argument name="newRequestPath" value="newrequestpath.html"/> + </actionGroup> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.xml new file mode 100644 index 0000000000000..b6f53524a14e7 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest.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="AdminCreateCategoryUrlRewriteAndAddTemporaryRedirectTest"> + <annotations> + <stories value="Create category URL rewrite"/> + <title value="Create category URL rewrite, with temporary redirect"/> + <description value="Login as admin and create category UrlRewrite with Temporary redirect"/> + <testCaseId value="MC-5336"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <createData entity="_defaultCategory" stepKey="category"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteRootCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Open Url Rewrite Index Page and update the Custom Url Rewrite, Store, Request Path, Redirect Type and Description --> + <actionGroup ref="AdminAddUrlRewrite" 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="newrequestpath.html"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!-- Assert Redirect path, Target Path and Redirect type in grid --> + <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <argument name="redirectPath" value="newrequestpath.html" /> + <argument name="redirectType" value="Temporary (302)" /> + <argument name="targetPath" value="$$category.name_lwr$$.html"/> + </actionGroup> + + <!--Assert Updated path directs to the category storefront --> + <actionGroup ref="StorefrontUrlRewriteRedirect" stepKey="openStorefrontUrlRedirectPath"> + <argument name="category" value="$$category.name$$"/> + <argument name="newRequestPath" value="newrequestpath.html"/> + </actionGroup> + </test> +</tests> \ No newline at end of file From df87bd74e8c90b82928279655e1597795d24650d Mon Sep 17 00:00:00 2001 From: Ji Lu <jilu1@adobe.com> Date: Fri, 15 Feb 2019 14:36:28 -0600 Subject: [PATCH 095/254] MC-10916: Update Customer Password on Storefront, Valid Current Password MC-10917: Update Customer Password on Storefront, Invalid Current Password MC-10918: Update Customer Password on Storefront, Invalid Confirmation Password --- .../LoginToStorefrontActionGroup.xml | 10 ---------- ...refrontWithEmailAndPasswordActionGroup.xml | 20 +++++++++++++++++++ .../StorefrontUpdateCustomerPasswordTest.xml | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontWithEmailAndPasswordActionGroup.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml index 79961b2bfc297..7be36ffbd9bc4 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontActionGroup.xml @@ -16,14 +16,4 @@ <fillField stepKey="fillPassword" userInput="{{Customer.password}}" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> </actionGroup> - <actionGroup name="LoginToStorefrontWithEmailAndPasswordActionGroup"> - <arguments> - <argument name="email" type="string"/> - <argument name="password" type="string"/> - </arguments> - <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> - <fillField stepKey="fillEmail" userInput="{{email}}" selector="{{StorefrontCustomerSignInFormSection.emailField}}"/> - <fillField stepKey="fillPassword" userInput="{{password}}" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> - <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontWithEmailAndPasswordActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontWithEmailAndPasswordActionGroup.xml new file mode 100644 index 0000000000000..33852fcf80864 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/LoginToStorefrontWithEmailAndPasswordActionGroup.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="LoginToStorefrontWithEmailAndPassword"> + <arguments> + <argument name="email" type="string"/> + <argument name="password" type="string"/> + </arguments> + <amOnPage stepKey="amOnSignInPage" url="{{StorefrontCustomerSignInPage.url}}"/> + <fillField stepKey="fillEmail" userInput="{{email}}" selector="{{StorefrontCustomerSignInFormSection.emailField}}"/> + <fillField stepKey="fillPassword" userInput="{{password}}" selector="{{StorefrontCustomerSignInFormSection.passwordField}}"/> + <click stepKey="clickSignInAccountButton" selector="{{StorefrontCustomerSignInFormSection.signInAccountButton}}"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml index a168a5f380538..38ea0de6f05a1 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml @@ -38,7 +38,7 @@ <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="LoginToStorefrontWithEmailAndPasswordActionGroup"> + <actionGroup stepKey="loginWithNewPassword" ref="LoginToStorefrontWithEmailAndPassword"> <argument name="email" value="$$customer.email$$"/> <argument name="password" value="$$customer.password$$#"/> </actionGroup> From 80cd335e35ad9827954492427db81db14b1c510f Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Fri, 15 Feb 2019 16:30:13 -0600 Subject: [PATCH 096/254] MAGETWO-98258: Fix elacticsearch search perfomance - reverting back fix --- .../Product/FieldProvider/DynamicField.php | 2 +- .../Aggregation/Builder/Term.php | 9 +-- .../SearchAdapter/Query/Builder/Match.php | 52 ++++--------- .../ValueTransformer/DateTransformer.php | 44 ----------- .../ValueTransformer/FloatTransformer.php | 24 ------ .../ValueTransformer/IntegerTransformer.php | 24 ------ .../ValueTransformer/TextTransformer.php | 65 ---------------- .../Query/ValueTransformerInterface.php | 22 ------ .../Query/ValueTransformerPool.php | 46 ----------- .../SearchAdapter/Query/Builder/MatchTest.php | 76 ++++++++++++------- app/code/Magento/Elasticsearch/etc/di.xml | 18 ----- .../Model/ResourceModel/SynonymReader.php | 14 +--- ...llback.php => text_attribute_rollback.php} | 6 +- .../_files/product_export_data_rollback.php | 8 +- ...uct_export_data_special_chars_rollback.php | 8 +- .../SearchAdapter/AdapterTest.php | 14 ---- .../Search/_files/filterable_attributes.php | 33 +------- .../_files/filterable_attributes_rollback.php | 12 +-- .../Search/Model/SynonymReaderTest.php | 17 +---- 19 files changed, 81 insertions(+), 413 deletions(-) delete mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php delete mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php delete mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/IntegerTransformer.php delete mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/TextTransformer.php delete mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php delete mode 100644 app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerPool.php rename dev/tests/integration/testsuite/Magento/Catalog/_files/{product_text_attribute_rollback.php => text_attribute_rollback.php} (84%) 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 9e2659a757924..c7e2a4beabb5c 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 @@ -126,7 +126,7 @@ public function getFields(array $context = []): array foreach ($groups as $group) { $groupPriceKey = $this->fieldNameResolver->getFieldName( $priceAttribute, - array_merge($context, ['customerGroupId' => $group->getId()]) + ['customerGroupId' => $group->getId(), 'websiteId' => $context['websiteId']] ); $allAttributes[$groupPriceKey] = [ 'type' => $this->fieldTypeConverter->convert(FieldTypeConverterInterface::INTERNAL_DATA_TYPE_FLOAT), diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php index eeb48f805bccf..bcfb7f5565b86 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php @@ -8,13 +8,10 @@ use Magento\Framework\Search\Request\BucketInterface as RequestBucketInterface; use Magento\Framework\Search\Dynamic\DataProviderInterface; -/** - * Builder for term buckets. - */ class Term implements BucketBuilderInterface { /** - * @inheritdoc + * {@inheritdoc} */ public function build( RequestBucketInterface $bucket, @@ -22,15 +19,13 @@ public function build( array $queryResult, DataProviderInterface $dataProvider ) { - $buckets = $queryResult['aggregations'][$bucket->getName()]['buckets'] ?? []; $values = []; - foreach ($buckets as $resultBucket) { + foreach ($queryResult['aggregations'][$bucket->getName()]['buckets'] as $resultBucket) { $values[$resultBucket['key']] = [ 'value' => $resultBucket['key'], 'count' => $resultBucket['doc_count'], ]; } - return $values; } } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index e83c49941bcc2..f1c3451482bab 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -5,16 +5,11 @@ */ namespace Magento\Elasticsearch\SearchAdapter\Query\Builder; -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; use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; -/** - * Builder for match query. - */ class Match implements QueryInterface { /** @@ -28,35 +23,24 @@ class Match implements QueryInterface private $fieldMapper; /** - * @deprecated - * @see \Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer * @var PreprocessorInterface[] */ protected $preprocessorContainer; - /** - * @var ValueTransformerPool - */ - private $valueTransformerPool; - /** * @param FieldMapperInterface $fieldMapper * @param PreprocessorInterface[] $preprocessorContainer - * @param ValueTransformerPool|null $valueTransformerPool */ public function __construct( FieldMapperInterface $fieldMapper, - array $preprocessorContainer, - ValueTransformerPool $valueTransformerPool = null + array $preprocessorContainer ) { $this->fieldMapper = $fieldMapper; $this->preprocessorContainer = $preprocessorContainer; - $this->valueTransformerPool = $valueTransformerPool ?? ObjectManager::getInstance() - ->get(ValueTransformerPool::class); } /** - * @inheritdoc + * {@inheritdoc} */ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $conditionType) { @@ -77,14 +61,16 @@ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $ } /** - * Prepare query. - * * @param string $queryValue * @param string $conditionType * @return array */ protected function prepareQuery($queryValue, $conditionType) { + $queryValue = $this->escape($queryValue); + foreach ($this->preprocessorContainer as $preprocessor) { + $queryValue = $preprocessor->process($queryValue); + } $condition = $conditionType === BoolExpression::QUERY_CONDITION_NOT ? self::QUERY_CONDITION_MUST_NOT : $conditionType; return [ @@ -113,34 +99,21 @@ protected function buildQueries(array $matches, array $queryValue) // Checking for quoted phrase \"phrase test\", trim escaped surrounding quotes if found $count = 0; - $value = preg_replace('#^"(.*)"$#m', '$1', $queryValue['value'], -1, $count); + $value = preg_replace('#^\\\\"(.*)\\\\"$#m', '$1', $queryValue['value'], -1, $count); $condition = ($count) ? 'match_phrase' : 'match'; - $attributesTypes = $this->fieldMapper->getAllAttributesTypes(); - $transformedTypes = []; foreach ($matches as $match) { $resolvedField = $this->fieldMapper->getFieldName( $match['field'], ['type' => FieldMapperInterface::TYPE_QUERY] ); - $valueTransformer = $this->valueTransformerPool->get($attributesTypes[$resolvedField]['type'] ?? 'text'); - $valueTransformerHash = \spl_object_hash($valueTransformer); - if (!isset($transformedTypes[$valueTransformerHash])) { - $transformedTypes[$valueTransformerHash] = $valueTransformer->transform($value); - } - $transformedValue = $transformedTypes[$valueTransformerHash]; - if (null === $transformedValue) { - //Value is incompatible with this field type. - continue; - } - $conditions[] = [ 'condition' => $queryValue['condition'], 'body' => [ $condition => [ $resolvedField => [ - 'query' => $transformedValue, - 'boost' => $match['boost'] ?? 1, + 'query' => $value, + 'boost' => isset($match['boost']) ? $match['boost'] : 1, ], ], ], @@ -151,15 +124,18 @@ protected function buildQueries(array $matches, array $queryValue) } /** + * Cut trailing plus or minus sign, and @ symbol, using of which causes InnoDB to report a syntax error. + * @link https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs. + * * 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) { + $value = preg_replace('/@+|[@+-]+$/', '', $value); + $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/'; $replace = '\\\$1'; diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.php deleted file mode 100644 index 49eca6e9d82a6..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/DateTransformer.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\Elasticsearch\SearchAdapter\Query\ValueTransformer; - -use Magento\Elasticsearch\Model\Adapter\FieldType\Date; -use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; - -/** - * Value transformer for date type fields. - */ -class DateTransformer implements ValueTransformerInterface -{ - /** - * @var Date - */ - private $dateFieldType; - - /** - * @param Date $dateFieldType - */ - public function __construct(Date $dateFieldType) - { - $this->dateFieldType = $dateFieldType; - } - - /** - * @inheritdoc - */ - public function transform(string $value): ?string - { - try { - $formattedDate = $this->dateFieldType->formatDate(null, $value); - } catch (\Exception $e) { - $formattedDate = null; - } - - return $formattedDate; - } -} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php deleted file mode 100644 index 5e330076d3df7..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/FloatTransformer.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer; - -use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; - -/** - * Value transformer for float type fields. - */ -class FloatTransformer implements ValueTransformerInterface -{ - /** - * @inheritdoc - */ - public function transform(string $value): ?float - { - return \is_numeric($value) ? (float) $value : null; - } -} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/IntegerTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/IntegerTransformer.php deleted file mode 100644 index 0846ff3a9bd86..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/IntegerTransformer.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer; - -use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; - -/** - * Value transformer for integer type fields. - */ -class IntegerTransformer implements ValueTransformerInterface -{ - /** - * @inheritdoc - */ - public function transform(string $value): ?int - { - return \is_numeric($value) ? (int) $value : null; - } -} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/TextTransformer.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/TextTransformer.php deleted file mode 100644 index 68bec2580f621..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformer/TextTransformer.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer; - -use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; -use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; - -/** - * Value transformer for fields with text types. - */ -class TextTransformer implements ValueTransformerInterface -{ - /** - * @var PreprocessorInterface[] - */ - private $preprocessors; - - /** - * @param PreprocessorInterface[] $preprocessors - */ - public function __construct(array $preprocessors = []) - { - foreach ($preprocessors as $preprocessor) { - if (!$preprocessor instanceof PreprocessorInterface) { - throw new \InvalidArgumentException( - \sprintf('"%s" is not a instance of ValueTransformerInterface.', get_class($preprocessor)) - ); - } - } - - $this->preprocessors = $preprocessors; - } - - /** - * @inheritdoc - */ - public function transform(string $value): string - { - $value = $this->escape($value); - foreach ($this->preprocessors as $preprocessor) { - $value = $preprocessor->process($value); - } - - return $value; - } - - /** - * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. - * - * @param string $value - * @return string - */ - private function escape(string $value): string - { - $pattern = '/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|"|~|\*|\?|:|\\\)/'; - $replace = '\\\$1'; - - return preg_replace($pattern, $replace, $value); - } -} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php deleted file mode 100644 index c84ddc69cc7a8..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\Elasticsearch\SearchAdapter\Query; - -/** - * Value transformer of search term for matching with ES field types. - */ -interface ValueTransformerInterface -{ - /** - * Transform value according to field type. - * - * @param string $value - * @return mixed - */ - public function transform(string $value); -} diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerPool.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerPool.php deleted file mode 100644 index 11a35d79ce1fd..0000000000000 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/ValueTransformerPool.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\Elasticsearch\SearchAdapter\Query; - -/** - * Pool of value transformers. - */ -class ValueTransformerPool -{ - /** - * @var ValueTransformerInterface[] - */ - private $transformers; - - /** - * @param ValueTransformerInterface[] $valueTransformers - */ - public function __construct(array $valueTransformers = []) - { - foreach ($valueTransformers as $valueTransformer) { - if (!$valueTransformer instanceof ValueTransformerInterface) { - throw new \InvalidArgumentException( - \sprintf('"%s" is not a instance of ValueTransformerInterface.', get_class($valueTransformer)) - ); - } - } - - $this->transformers = $valueTransformers; - } - - /** - * Get value transformer related to field type. - * - * @param string $fieldType - * @return ValueTransformerInterface - */ - public function get(string $fieldType): ValueTransformerInterface - { - return $this->transformers[$fieldType] ?? $this->transformers['default']; - } -} diff --git a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php index c8aa3db39bd5e..8114feb09d35d 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/SearchAdapter/Query/Builder/MatchTest.php @@ -7,8 +7,6 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; use Magento\Elasticsearch\SearchAdapter\Query\Builder\Match as MatchQueryBuilder; -use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerInterface; -use Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool; use Magento\Framework\Search\Request\Query\Match as MatchRequestQuery; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit_Framework_MockObject_MockObject as MockObject; @@ -25,48 +23,46 @@ class MatchTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $valueTransformerPoolMock = $this->createMock(ValueTransformerPool::class); - $valueTransformerMock = $this->createMock(ValueTransformerInterface::class); - $valueTransformerPoolMock->method('get') - ->willReturn($valueTransformerMock); - $valueTransformerMock->method('transform') - ->willReturnArgument(0); - $this->matchQueryBuilder = (new ObjectManager($this))->getObject( MatchQueryBuilder::class, [ 'fieldMapper' => $this->getFieldMapper(), 'preprocessorContainer' => [], - 'valueTransformerPool' => $valueTransformerPoolMock, ] ); } /** * Tests that method constructs a correct select query. - * * @see MatchQueryBuilder::build + * + * @dataProvider queryValuesInvariantsProvider + * + * @param string $rawQueryValue + * @param string $errorMessage */ - public function testBuild() + public function testBuild($rawQueryValue, $errorMessage) { - $rawQueryValue = 'query_value'; - $selectQuery = $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'not'); + $this->assertSelectQuery( + $this->matchQueryBuilder->build([], $this->getMatchRequestQuery($rawQueryValue), 'not'), + $errorMessage + ); + } - $expectedSelectQuery = [ - 'bool' => [ - 'must_not' => [ - [ - 'match' => [ - 'some_field' => [ - 'query' => $rawQueryValue, - 'boost' => 43, - ], - ], - ], - ], - ], + /** + * @link https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs. + * + * @return array + */ + public function queryValuesInvariantsProvider() + { + return [ + ['query_value', 'Select query field must match simple raw query value.'], + ['query_value+', 'Specifying a trailing plus sign causes InnoDB to report a syntax error.'], + ['query_value-', 'Specifying a trailing minus sign causes InnoDB to report a syntax error.'], + ['query_@value', 'The @ symbol is reserved for use by the @distance proximity search operator.'], + ['query_value+@', 'The @ symbol is reserved for use by the @distance proximity search operator.'], ]; - $this->assertEquals($expectedSelectQuery, $selectQuery); } /** @@ -115,6 +111,30 @@ public function matchProvider() ]; } + /** + * @param array $selectQuery + * @param string $errorMessage + */ + private function assertSelectQuery($selectQuery, $errorMessage) + { + $expectedSelectQuery = [ + 'bool' => [ + 'must_not' => [ + [ + 'match' => [ + 'some_field' => [ + 'query' => 'query_value', + 'boost' => 43, + ], + ], + ], + ], + ], + ]; + + $this->assertEquals($expectedSelectQuery, $selectQuery, $errorMessage); + } + /** * Gets fieldMapper mock object. * diff --git a/app/code/Magento/Elasticsearch/etc/di.xml b/app/code/Magento/Elasticsearch/etc/di.xml index 05a67605ba0e6..7e219bb2f918f 100644 --- a/app/code/Magento/Elasticsearch/etc/di.xml +++ b/app/code/Magento/Elasticsearch/etc/di.xml @@ -421,22 +421,4 @@ </argument> </arguments> </type> - <type name="Magento\Elasticsearch\SearchAdapter\Query\ValueTransformerPool"> - <arguments> - <argument name="valueTransformers" xsi:type="array"> - <item name="default" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer</item> - <item name="date" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\DateTransformer</item> - <item name="float" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\FloatTransformer</item> - <item name="integer" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\IntegerTransformer</item> - </argument> - </arguments> - </type> - <type name="Magento\Elasticsearch\SearchAdapter\Query\ValueTransformer\TextTransformer"> - <arguments> - <argument name="preprocessors" xsi:type="array"> - <item name="stopwordsPreprocessor" xsi:type="object">Magento\Elasticsearch\SearchAdapter\Query\Preprocessor\Stopwords</item> - <item name="synonymsPreprocessor" xsi:type="object">Magento\Search\Adapter\Query\Preprocessor\Synonyms</item> - </argument> - </arguments> - </type> </config> diff --git a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php index 45eee0a4001d1..46e794a1954cf 100644 --- a/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php +++ b/app/code/Magento/Search/Model/ResourceModel/SynonymReader.php @@ -87,7 +87,7 @@ private function queryByPhrase($phrase) { $matchQuery = $this->fullTextSelect->getMatchQuery( ['synonyms' => 'synonyms'], - $this->escapePhrase($phrase), + $phrase, Fulltext::FULLTEXT_MODE_BOOLEAN ); $query = $this->getConnection()->select()->from( @@ -97,18 +97,6 @@ private function queryByPhrase($phrase) return $this->getConnection()->fetchAll($query); } - /** - * Cut trailing plus or minus sign, and @ symbol, using of which causes InnoDB to report a syntax error. - * - * @see https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html - * @param string $phrase - * @return string - */ - private function escapePhrase(string $phrase): string - { - return preg_replace('/@+|[@+-]+$/', '', $phrase); - } - /** * A private helper function to retrieve matching synonym groups per scope * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php similarity index 84% rename from dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute_rollback.php rename to dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php index a9ab0e11312b2..cbc0476efd1b5 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_text_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/text_attribute_rollback.php @@ -3,15 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - /* Delete attribute with text_attribute code */ -$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry'); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); - /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + 'Magento\Catalog\Model\ResourceModel\Eav\Attribute' ); $attribute->load('text_attribute', 'attribute_code'); $attribute->delete(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php index c57c7c3fd6a92..168073bc6ab74 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_rollback.php @@ -5,10 +5,10 @@ */ /** Delete all products */ -include dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php'; +require dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php'; /** Delete text attribute */ -include dirname(dirname(__DIR__)) . '/Catalog/_files/product_text_attribute_rollback.php'; +require dirname(dirname(__DIR__)) . '/Catalog/_files/text_attribute_rollback.php'; -include dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php'; +require dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php'; -include dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; +require dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php index c57c7c3fd6a92..168073bc6ab74 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data_special_chars_rollback.php @@ -5,10 +5,10 @@ */ /** Delete all products */ -include dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php'; +require dirname(dirname(__DIR__)) . '/Catalog/_files/products_with_multiselect_attribute_rollback.php'; /** Delete text attribute */ -include dirname(dirname(__DIR__)) . '/Catalog/_files/product_text_attribute_rollback.php'; +require dirname(dirname(__DIR__)) . '/Catalog/_files/text_attribute_rollback.php'; -include dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php'; +require dirname(dirname(__DIR__)) . '/Store/_files/second_store_rollback.php'; -include dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; +require dirname(dirname(__DIR__)) . '/Catalog/_files/category_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php index 8d80fd8533d6f..dc288a18fadb7 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/SearchAdapter/AdapterTest.php @@ -369,18 +369,4 @@ public function dateDataProvider() [['from' => '2000-02-01T00:00:00Z', 'to' => ''], 0], ]; } - - public function filterByAttributeValuesDataProvider() - { - $variations = parent::filterByAttributeValuesDataProvider(); - - $variations['quick search by date'] = [ - 'quick_search_container', - [ - 'search_term' => '2000-10-30', - ], - ]; - - return $variations; - } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php index f4f3337a253c0..b09af48b5f943 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes.php @@ -20,10 +20,6 @@ CategorySetup::class, ['resourceName' => 'catalog_setup'] ); -$productEntityTypeId = $installer->getEntityTypeId( - \Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE -); - $selectOptions = []; $selectAttributes = []; foreach (range(1, 2) as $index) { @@ -34,7 +30,7 @@ $selectAttribute->setData( [ 'attribute_code' => 'select_attribute_' . $index, - 'entity_type_id' => $productEntityTypeId, + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), 'is_global' => 1, 'is_user_defined' => 1, 'frontend_input' => 'select', @@ -60,8 +56,7 @@ ); $selectAttribute->save(); /* Assign attribute to attribute set */ - $installer->addAttributeToGroup($productEntityTypeId, 'Default', 'General', $selectAttribute->getId()); - + $installer->addAttributeToGroup('catalog_product', 'Default', 'General', $selectAttribute->getId()); /** @var $selectOptions Collection */ $selectOption = Bootstrap::getObjectManager()->create( Collection::class @@ -70,26 +65,6 @@ $selectAttributes[$index] = $selectAttribute; $selectOptions[$index] = $selectOption; } - -$dateAttribute = Bootstrap::getObjectManager()->create(Attribute::class); -$dateAttribute->setData( - [ - 'attribute_code' => 'date_attribute', - 'entity_type_id' => $productEntityTypeId, - 'is_global' => 1, - 'is_filterable' => 1, - 'backend_type' => 'datetime', - 'frontend_input' => 'date', - 'frontend_label' => 'Test Date', - 'is_searchable' => 1, - 'is_filterable_in_search' => 1, - ] -); -$dateAttribute->save(); -/* Assign attribute to attribute set */ -$installer->addAttributeToGroup($productEntityTypeId, 'Default', 'General', $dateAttribute->getId()); - -$productAttributeSetId = $installer->getAttributeSetId($productEntityTypeId, 'Default'); /* Create simple products per each first attribute option */ foreach ($selectOptions[1] as $option) { /** @var $product Product */ @@ -99,7 +74,7 @@ $product->setTypeId( Type::TYPE_SIMPLE )->setAttributeSetId( - $productAttributeSetId + $installer->getAttributeSetId('catalog_product', 'Default') )->setWebsiteIds( [1] )->setName( @@ -117,7 +92,6 @@ )->setStockData( ['use_config_manage_stock' => 1, 'qty' => 5, 'is_in_stock' => 1] )->save(); - Bootstrap::getObjectManager()->get( Action::class )->updateAttributes( @@ -125,7 +99,6 @@ [ $selectAttributes[1]->getAttributeCode() => $option->getId(), $selectAttributes[2]->getAttributeCode() => $selectOptions[2]->getLastItem()->getId(), - $dateAttribute->getAttributeCode() => '10/30/2000', ], $product->getStoreId() ); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php index fd413726b2637..18a5372d06d98 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attributes_rollback.php @@ -13,7 +13,6 @@ $registry = Bootstrap::getObjectManager()->get(Registry::class); $registry->unregister('isSecureArea'); $registry->register('isSecureArea', true); - /** @var $productCollection \Magento\Catalog\Model\ResourceModel\Product\Collection */ $productCollection = Bootstrap::getObjectManager() ->create(Product::class) @@ -21,26 +20,17 @@ foreach ($productCollection as $product) { $product->delete(); } - /** @var $attribute Attribute */ $attribute = Bootstrap::getObjectManager()->create( Attribute::class ); /** @var $installer CategorySetup */ $installer = Bootstrap::getObjectManager()->create(CategorySetup::class); -$productEntityTypeId = $installer->getEntityTypeId( - \Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE -); foreach (range(1, 2) as $index) { - $attribute->loadByCode($productEntityTypeId, 'select_attribute_' . $index); + $attribute->loadByCode($installer->getEntityTypeId('catalog_product'), 'select_attribute_' . $index); if ($attribute->getId()) { $attribute->delete(); } } -$attribute->loadByCode($productEntityTypeId, 'date_attribute'); -if ($attribute->getId()) { - $attribute->delete(); -} - $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php index 2d0020ba22680..b9ba89ba53144 100644 --- a/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php +++ b/dev/tests/integration/testsuite/Magento/Search/Model/SynonymReaderTest.php @@ -48,22 +48,7 @@ public static function loadByPhraseDataProvider() ['synonyms' => 'queen,monarch', 'store_id' => 1, 'website_id' => 0], ['synonyms' => 'british,english', 'store_id' => 1, 'website_id' => 0] ] - ], - [ - 'query_value', [] - ], - [ - 'query_value+', [] - ], - [ - 'query_value-', [] - ], - [ - 'query_@value', [] - ], - [ - 'query_value+@', [] - ], + ] ]; } From e7e2f96b74675b0440bf1ee8d70160acb818f520 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Fri, 15 Feb 2019 16:40:00 -0600 Subject: [PATCH 097/254] ENGCOM-4289: GraphQL 2.3.1 release fixes --- .../Model/Cart/AddSimpleProductToCart.php | 11 +- .../Cart/Address/AddressDataProvider.php | 94 ----- .../Address/BillingAddressDataProvider.php | 72 ---- .../Address/ShippingAddressesDataProvider.php | 76 ---- ...Address.php => ExtractDataFromAddress.php} | 32 +- .../Model/Cart/ExtractDataFromCart.php | 18 + .../Model/Cart/GetCustomerAddress.php | 65 ++++ .../Model/Cart/SetBillingAddressOnCart.php | 28 +- .../Model/Cart/SetShippingAddressOnCart.php | 29 +- .../Model/Resolver/BillingAddress.php | 24 +- .../Model/Resolver/CartAddresses.php | 48 --- .../Resolver/SetShippingAddressesOnCart.php | 13 +- .../Model/Resolver/ShippingAddresses.php | 26 +- .../Magento/QuoteGraphQl/etc/schema.graphqls | 31 +- .../Quote/AddSimpleProductToCartTest.php | 86 +++++ .../Magento/GraphQl/Quote/GetCartTest.php | 10 +- .../Quote/SetBillingAddressOnCartTest.php | 244 ++++++++---- .../Quote/SetShippingAddressOnCartTest.php | 357 ++++++++++-------- .../Quote/SetShippingMethodOnCartTest.php | 252 ------------- 19 files changed, 662 insertions(+), 854 deletions(-) delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Address/BillingAddressDataProvider.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Address/ShippingAddressesDataProvider.php rename app/code/Magento/QuoteGraphQl/Model/Cart/{Address/Mapper/Address.php => ExtractDataFromAddress.php} (64%) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php delete mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php index 2303d2aa14f03..1b32866ed883c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AddSimpleProductToCart.php @@ -75,7 +75,16 @@ public function execute(Quote $cart, array $cartItemData): void throw new GraphQlNoSuchEntityException(__('Could not find a product with SKU "%sku"', ['sku' => $sku])); } - $result = $cart->addProduct($product, $this->createBuyRequest($qty, $customizableOptions)); + try { + $result = $cart->addProduct($product, $this->createBuyRequest($qty, $customizableOptions)); + } catch (\Exception $e) { + throw new GraphQlInputException( + __( + 'Could not add the product with SKU %sku to the shopping cart: %message', + ['sku' => $sku, 'message' => $e->getMessage()] + ) + ); + } if (is_string($result)) { throw new GraphQlInputException(__($result)); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php deleted file mode 100644 index fb742477ec99b..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/AddressDataProvider.php +++ /dev/null @@ -1,94 +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\Framework\Api\ExtensibleDataObjectConverter; -use Magento\Quote\Api\Data\AddressInterface; -use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Model\Quote\Address as QuoteAddress; - -/** - * Class AddressDataProvider - * - * Collect and return information about cart shipping and billing addresses - */ -class AddressDataProvider -{ - /** - * @var ExtensibleDataObjectConverter - */ - private $dataObjectConverter; - - /** - * AddressDataProvider constructor. - * - * @param ExtensibleDataObjectConverter $dataObjectConverter - */ - public function __construct( - ExtensibleDataObjectConverter $dataObjectConverter - ) { - $this->dataObjectConverter = $dataObjectConverter; - } - - /** - * Collect and return information about shipping and billing addresses - * - * @param CartInterface $cart - * @return array - */ - public function getCartAddresses(CartInterface $cart): array - { - $addressData = []; - $shippingAddress = $cart->getShippingAddress(); - $billingAddress = $cart->getBillingAddress(); - - if ($shippingAddress) { - $shippingData = $this->dataObjectConverter->toFlatArray($shippingAddress, [], AddressInterface::class); - $shippingData['address_type'] = 'SHIPPING'; - $addressData[] = array_merge($shippingData, $this->extractAddressData($shippingAddress)); - } - - if ($billingAddress) { - $billingData = $this->dataObjectConverter->toFlatArray($billingAddress, [], AddressInterface::class); - $billingData['address_type'] = 'BILLING'; - $addressData[] = array_merge($billingData, $this->extractAddressData($billingAddress)); - } - - return $addressData; - } - - /** - * Extract the necessary address fields from address model - * - * @param QuoteAddress $address - * @return array - */ - private function extractAddressData(QuoteAddress $address): array - { - $addressData = [ - 'country' => [ - 'code' => $address->getCountryId(), - 'label' => $address->getCountry() - ], - 'region' => [ - 'code' => $address->getRegionCode(), - 'label' => $address->getRegion() - ], - 'street' => $address->getStreet(), - 'selected_shipping_method' => [ - 'code' => $address->getShippingMethod(), - 'label' => $address->getShippingDescription(), - 'free_shipping' => $address->getFreeShipping(), - ], - 'items_weight' => $address->getWeight(), - 'customer_notes' => $address->getCustomerNotes() - ]; - - return $addressData; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/BillingAddressDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/BillingAddressDataProvider.php deleted file mode 100644 index bcd781025a6d6..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/BillingAddressDataProvider.php +++ /dev/null @@ -1,72 +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\Framework\Api\ExtensibleDataObjectConverter; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\AddressInterface; -use Magento\Quote\Api\Data\CartInterface; -use Magento\QuoteGraphQl\Model\Cart\Address\Mapper\Address; - -/** - * Collect and return information about a billing address - */ -class BillingAddressDataProvider -{ - /** - * @var CartRepositoryInterface - */ - private $cartRepository; - - /** - * @var Address - */ - private $addressMapper; - - /** - * @var ExtensibleDataObjectConverter - */ - private $dataObjectConverter; - - /** - * AddressDataProvider constructor. - * - * @param CartRepositoryInterface $cartRepository - * @param Address $addressMapper - * @param ExtensibleDataObjectConverter $dataObjectConverter - */ - public function __construct( - CartRepositoryInterface $cartRepository, - Address $addressMapper, - ExtensibleDataObjectConverter $dataObjectConverter - ) { - $this->cartRepository = $cartRepository; - $this->addressMapper = $addressMapper; - $this->dataObjectConverter = $dataObjectConverter; - } - - /** - * Collect and return information about a billing addresses - * - * @param CartInterface $cart - * @return null|array - */ - public function getCartAddresses(CartInterface $cart): ?array - { - $cart = $this->cartRepository->get($cart->getId()); - $billingAddress = $cart->getBillingAddress(); - - if (!$billingAddress) { - return null; - } - $billingData = $this->dataObjectConverter->toFlatArray($billingAddress, [], AddressInterface::class); - $addressData = array_merge($billingData, $this->addressMapper->toNestedArray($billingAddress)); - - return $addressData; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/ShippingAddressesDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Address/ShippingAddressesDataProvider.php deleted file mode 100644 index eb9d760809594..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/ShippingAddressesDataProvider.php +++ /dev/null @@ -1,76 +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\Framework\Api\ExtensibleDataObjectConverter; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\AddressInterface; -use Magento\Quote\Api\Data\CartInterface; -use Magento\QuoteGraphQl\Model\Cart\Address\Mapper\Address; - -/** - * Class AddressDataProvider - * - * Collect and return information about cart shipping and billing addresses - */ -class ShippingAddressesDataProvider -{ - /** - * @var ExtensibleDataObjectConverter - */ - private $dataObjectConverter; - - /** - * @var CartRepositoryInterface - */ - private $cartRepository; - - /** - * @var Address - */ - private $addressMapper; - - /** - * AddressDataProvider constructor. - * - * @param ExtensibleDataObjectConverter $dataObjectConverter - * @param CartRepositoryInterface $cartRepository - * @param Address $addressMapper - */ - public function __construct( - ExtensibleDataObjectConverter $dataObjectConverter, - CartRepositoryInterface $cartRepository, - Address $addressMapper - ) { - $this->dataObjectConverter = $dataObjectConverter; - $this->cartRepository = $cartRepository; - $this->addressMapper = $addressMapper; - } - - /** - * Collect and return information about shipping addresses - * - * @param CartInterface $cart - * @return array - */ - public function getCartAddresses(CartInterface $cart): array - { - $cart = $this->cartRepository->get($cart->getId()); - $addressData = []; - $shippingAddresses = $cart->getAllShippingAddresses(); - - if ($shippingAddresses) { - foreach ($shippingAddresses as $shippingAddress) { - $shippingData = $this->dataObjectConverter->toFlatArray($shippingAddress, [], AddressInterface::class); - $addressData[] = array_merge($shippingData, $this->addressMapper->toNestedArray($shippingAddress)); - } - } - - return $addressData; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/Mapper/Address.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php similarity index 64% rename from app/code/Magento/QuoteGraphQl/Model/Cart/Address/Mapper/Address.php rename to app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php index 64ac407e2692a..b0e5070315d87 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Address/Mapper/Address.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php @@ -5,26 +5,42 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Cart\Address\Mapper; +namespace Magento\QuoteGraphQl\Model\Cart; +use Magento\Framework\Api\ExtensibleDataObjectConverter; +use Magento\Quote\Api\Data\AddressInterface; use Magento\Quote\Model\Quote\Address as QuoteAddress; /** - * Class Address - * * Extract the necessary address fields from an Address model */ -class Address +class ExtractDataFromAddress { /** - * Converts Address model data to nested array + * @var ExtensibleDataObjectConverter + */ + private $dataObjectConverter; + + /** + * @param ExtensibleDataObjectConverter $dataObjectConverter + */ + public function __construct(ExtensibleDataObjectConverter $dataObjectConverter) + { + $this->dataObjectConverter = $dataObjectConverter; + } + + /** + * Converts Address model to flat array * * @param QuoteAddress $address * @return array */ - public function toNestedArray(QuoteAddress $address): array + public function execute(QuoteAddress $address): array { - $addressData = [ + $addressData = $this->dataObjectConverter->toFlatArray($address, [], AddressInterface::class); + $addressData['model'] = $address; + + $addressData = array_merge($addressData, [ 'country' => [ 'code' => $address->getCountryId(), 'label' => $address->getCountry() @@ -41,7 +57,7 @@ public function toNestedArray(QuoteAddress $address): array ], 'items_weight' => $address->getWeight(), 'customer_notes' => $address->getCustomerNotes() - ]; + ]); if (!$address->hasItems()) { return $addressData; diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromCart.php index 438f28980918d..62ffdbd4b194f 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromCart.php @@ -7,19 +7,36 @@ namespace Magento\QuoteGraphQl\Model\Cart; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Item as QuoteItem; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; /** * Extract data from cart */ class ExtractDataFromCart { + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedQuoteId; + + /** + * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + */ + public function __construct( + QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId + ) { + $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; + } + /** * Extract data from cart * * @param Quote $cart * @return array + * @throws NoSuchEntityException */ public function execute(Quote $cart): array { @@ -43,6 +60,7 @@ public function execute(Quote $cart): array $appliedCoupon = $cart->getCouponCode(); return [ + 'cart_id' => $this->quoteIdToMaskedQuoteId->execute((int)$cart->getId()), 'items' => $items, 'applied_coupon' => $appliedCoupon ? ['code' => $appliedCoupon] : null ]; diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php new file mode 100644 index 0000000000000..d3de86702b96c --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; + +/** + * Get customer address. Throws exception if customer is not owner of address + */ +class GetCustomerAddress +{ + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @param AddressRepositoryInterface $addressRepository + */ + public function __construct(AddressRepositoryInterface $addressRepository) + { + $this->addressRepository = $addressRepository; + } + + /** + * Get customer address. Throws exception if customer is not owner of address + * + * @param int $addressId + * @param int $customerId + * @return AddressInterface + * @throws GraphQlAuthorizationException + * @throws GraphQlNoSuchEntityException + * @throws LocalizedException + */ + public function execute(int $addressId, int $customerId): AddressInterface + { + try { + $customerAddress = $this->addressRepository->getById($addressId); + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException( + __('Could not find a address with ID "%address_id"', ['address_id' => $addressId]) + ); + } + + if ((int)$customerAddress->getCustomerId() !== $customerId) { + throw new GraphQlAuthorizationException( + __( + 'The current user cannot use address with ID "%address_id"', + ['address_id' => $addressId] + ) + ); + } + return $customerAddress; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 97b1ed09decc4..5d3125ae13ef8 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -7,7 +7,6 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Customer\Api\Data\AddressInterface; use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; @@ -26,11 +25,6 @@ class SetBillingAddressOnCart */ private $billingAddressManagement; - /** - * @var AddressRepositoryInterface - */ - private $addressRepository; - /** * @var Address */ @@ -41,26 +35,40 @@ class SetBillingAddressOnCart */ private $checkCustomerAccount; + /** + * @var GetCustomerAddress + */ + private $getCustomerAddress; + /** * @param BillingAddressManagementInterface $billingAddressManagement * @param AddressRepositoryInterface $addressRepository * @param Address $addressModel * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomerAddress $getCustomerAddress */ public function __construct( BillingAddressManagementInterface $billingAddressManagement, AddressRepositoryInterface $addressRepository, Address $addressModel, - CheckCustomerAccount $checkCustomerAccount + CheckCustomerAccount $checkCustomerAccount, + GetCustomerAddress $getCustomerAddress ) { $this->billingAddressManagement = $billingAddressManagement; $this->addressRepository = $addressRepository; $this->addressModel = $addressModel; $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomerAddress = $getCustomerAddress; } /** - * @inheritdoc + * Set billing address for a specified shopping cart + * + * @param ContextInterface $context + * @param CartInterface $cart + * @param array $billingAddress + * @return void + * @throws GraphQlInputException */ public function execute(ContextInterface $context, CartInterface $cart, array $billingAddress): void { @@ -88,9 +96,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b $billingAddress = $this->addressModel->addData($addressInput); } else { $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType()); - - /** @var AddressInterface $customerAddress */ - $customerAddress = $this->addressRepository->getById($customerAddressId); + $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$context->getUserId()); $billingAddress = $this->addressModel->importCustomerAddressData($customerAddress); } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php index b9fd5c7807d2f..e6b18fc88a27a 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php @@ -7,14 +7,12 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Customer\Api\Data\AddressInterface; use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Quote\Api\Data\CartInterface; use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\ShippingAddressManagementInterface; -use Magento\Customer\Api\AddressRepositoryInterface; /** * Set single shipping address for a specified shopping cart @@ -26,11 +24,6 @@ class SetShippingAddressOnCart implements SetShippingAddressesOnCartInterface */ private $shippingAddressManagement; - /** - * @var AddressRepositoryInterface - */ - private $addressRepository; - /** * @var Address */ @@ -41,26 +34,36 @@ class SetShippingAddressOnCart implements SetShippingAddressesOnCartInterface */ private $checkCustomerAccount; + /** + * @var GetCustomerAddress + */ + private $getCustomerAddress; + /** * @param ShippingAddressManagementInterface $shippingAddressManagement - * @param AddressRepositoryInterface $addressRepository * @param Address $addressModel * @param CheckCustomerAccount $checkCustomerAccount + * @param GetCustomerAddress $getCustomerAddress */ public function __construct( ShippingAddressManagementInterface $shippingAddressManagement, - AddressRepositoryInterface $addressRepository, Address $addressModel, - CheckCustomerAccount $checkCustomerAccount + CheckCustomerAccount $checkCustomerAccount, + GetCustomerAddress $getCustomerAddress ) { $this->shippingAddressManagement = $shippingAddressManagement; - $this->addressRepository = $addressRepository; $this->addressModel = $addressModel; $this->checkCustomerAccount = $checkCustomerAccount; + $this->getCustomerAddress = $getCustomerAddress; } /** * @inheritdoc + * + * @param ContextInterface $context + * @param CartInterface $cart + * @param array $shippingAddresses + * @throws GraphQlInputException */ public function execute(ContextInterface $context, CartInterface $cart, array $shippingAddresses): void { @@ -87,9 +90,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s $shippingAddress = $this->addressModel->addData($addressInput); } else { $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType()); - - /** @var AddressInterface $customerAddress */ - $customerAddress = $this->addressRepository->getById($customerAddressId); + $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$context->getUserId()); $shippingAddress = $this->addressModel->importCustomerAddressData($customerAddress); } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php index 7de5a4ac05c24..a03533ecefffa 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/BillingAddress.php @@ -11,7 +11,7 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\QuoteGraphQl\Model\Cart\Address\BillingAddressDataProvider; +use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromAddress; /** * @inheritdoc @@ -19,17 +19,16 @@ class BillingAddress implements ResolverInterface { /** - * @var BillingAddressDataProvider + * @var ExtractDataFromAddress */ - private $addressDataProvider; + private $extractDataFromAddress; /** - * @param BillingAddressDataProvider $addressDataProvider + * @param ExtractDataFromAddress $extractDataFromAddress */ - public function __construct( - BillingAddressDataProvider $addressDataProvider - ) { - $this->addressDataProvider = $addressDataProvider; + public function __construct(ExtractDataFromAddress $extractDataFromAddress) + { + $this->extractDataFromAddress = $extractDataFromAddress; } /** @@ -40,9 +39,14 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!isset($value['model'])) { throw new LocalizedException(__('"model" value should be specified')); } - $cart = $value['model']; - return $this->addressDataProvider->getCartAddresses($cart); + $billingAddress = $cart->getBillingAddress(); + if (null === $billingAddress) { + return null; + } + + $addressData = $this->extractDataFromAddress->execute($billingAddress); + return $addressData; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php deleted file mode 100644 index 69544672bf12e..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartAddresses.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Resolver; - -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\QuoteGraphQl\Model\Cart\Address\AddressDataProvider; - -/** - * @inheritdoc - */ -class CartAddresses implements ResolverInterface -{ - /** - * @var AddressDataProvider - */ - private $addressDataProvider; - - /** - * @param AddressDataProvider $addressDataProvider - */ - public function __construct( - AddressDataProvider $addressDataProvider - ) { - $this->addressDataProvider = $addressDataProvider; - } - - /** - * @inheritdoc - */ - public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) - { - if (!isset($value['model'])) { - throw new LocalizedException(__('"model" value should be specified')); - } - - $cart = $value['model']; - - return $this->addressDataProvider->getCartAddresses($cart); - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php index b024e7b77af40..ec50bd6ab6ea4 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php @@ -12,7 +12,6 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; use Magento\Quote\Model\ShippingAddressManagementInterface; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; use Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface; @@ -24,11 +23,6 @@ */ class SetShippingAddressesOnCart implements ResolverInterface { - /** - * @var MaskedQuoteIdToQuoteIdInterface - */ - private $maskedQuoteIdToQuoteId; - /** * @var ShippingAddressManagementInterface */ @@ -50,20 +44,17 @@ class SetShippingAddressesOnCart implements ResolverInterface private $setShippingAddressesOnCart; /** - * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId * @param ShippingAddressManagementInterface $shippingAddressManagement * @param GetCartForUser $getCartForUser * @param ArrayManager $arrayManager * @param SetShippingAddressesOnCartInterface $setShippingAddressesOnCart */ public function __construct( - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, ShippingAddressManagementInterface $shippingAddressManagement, GetCartForUser $getCartForUser, ArrayManager $arrayManager, SetShippingAddressesOnCartInterface $setShippingAddressesOnCart ) { - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->shippingAddressManagement = $shippingAddressManagement; $this->getCartForUser = $getCartForUser; $this->arrayManager = $arrayManager; @@ -76,16 +67,16 @@ public function __construct( public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { $shippingAddresses = $this->arrayManager->get('input/shipping_addresses', $args); - $maskedCartId = $this->arrayManager->get('input/cart_id', $args); + $maskedCartId = (string) $this->arrayManager->get('input/cart_id', $args); if (!$maskedCartId) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } + if (!$shippingAddresses) { throw new GraphQlInputException(__('Required parameter "shipping_addresses" is missing')); } - $maskedCartId = $args['input']['cart_id']; $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); $this->setShippingAddressesOnCart->execute($context, $cart, $shippingAddresses); diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php index caa0eee22d702..3a55ef9ae25a8 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddresses.php @@ -11,7 +11,7 @@ use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\QuoteGraphQl\Model\Cart\Address\ShippingAddressesDataProvider; +use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromAddress; /** * @inheritdoc @@ -19,17 +19,16 @@ class ShippingAddresses implements ResolverInterface { /** - * @var ShippingAddressesDataProvider + * @var ExtractDataFromAddress */ - private $addressDataProvider; + private $extractDataFromAddress; /** - * @param ShippingAddressesDataProvider $addressDataProvider + * @param ExtractDataFromAddress $extractDataFromAddress */ - public function __construct( - ShippingAddressesDataProvider $addressDataProvider - ) { - $this->addressDataProvider = $addressDataProvider; + public function __construct(ExtractDataFromAddress $extractDataFromAddress) + { + $this->extractDataFromAddress = $extractDataFromAddress; } /** @@ -40,9 +39,16 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!isset($value['model'])) { throw new LocalizedException(__('"model" value should be specified')); } - $cart = $value['model']; - return $this->addressDataProvider->getCartAddresses($cart); + $addressesData = []; + $shippingAddresses = $cart->getAllShippingAddresses(); + + if (count($shippingAddresses)) { + foreach ($shippingAddresses as $shippingAddress) { + $addressesData[] = $this->extractDataFromAddress->execute($shippingAddress); + } + } + return $addressesData; } } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 3448d640618d4..17dd1141a0368 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -2,7 +2,7 @@ # See COPYING.txt for license details. type Query { - Cart(cart_id: String!): Cart @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart") @doc(description:"Returns information about shopping cart") + cart(cart_id: String!): Cart @resolver (class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Cart") @doc(description:"Returns information about shopping cart") } type Mutation { @@ -64,8 +64,8 @@ input SetShippingMethodsOnCartInput { input ShippingMethodForAddressInput { cart_address_id: Int! - shipping_carrier_code: String! - shipping_method_code: String! + carrier_code: String! + method_code: String! } type SetBillingAddressOnCartOutput { @@ -116,8 +116,6 @@ type CartAddress { country: CartAddressCountry telephone: String address_type: AdressTypeEnum - selected_shipping_method: CheckoutShippingMethod - available_shipping_methods: [CheckoutShippingMethod] items_weight: Float customer_notes: String cart_items: [CartItemQuantity] @@ -138,12 +136,20 @@ type CartAddressCountry { label: String } -type CheckoutShippingMethod { - code: String - label: String - free_shipping: Boolean! +type SelectedShippingMethod { + amount: Float! +} + +type AvailableShippingMethod { + carrier_code: String! + carrier_title: String! + method_code: String! + method_title: String! error_message: String - # TODO: Add more complex structure for shipping rates + amount: Float! + base_amount: Float! + price_excl_tax: Float! + price_incl_tax: Float! } enum AdressTypeEnum { @@ -219,8 +225,3 @@ type CartItemSelectedOptionValuePrice { units: String! type: PriceTypeEnum! } - -input CartItemDetailsInput { - sku: String! - qty: Float! -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php new file mode 100644 index 0000000000000..4cbc614e1b8dc --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; + +class AddSimpleProductToCartTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var Quote + */ + private $quote; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/products.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @expectedException \Exception + * @expectedExceptionMessage The requested qty is not available + */ + public function testAddProductIfQuantityIsNotAvailable() + { + $sku = 'simple'; + $qty = 200; + + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + + $query = <<<QUERY +mutation { + addSimpleProductsToCart( + input: { + cart_id: "{$maskedQuoteId}", + cartItems: [ + { + data: { + qty: $qty + sku: "$sku" + } + } + ] + } + ) { + cart { + cart_id + } + } +} +QUERY; + + $this->graphQlQuery($query); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php index f0038351bcdcf..44cd2ef526bd5 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php @@ -68,9 +68,9 @@ public function testGetOwnCartForRegisteredCustomer() $response = $this->sendRequestWithToken($query); - self::assertArrayHasKey('Cart', $response); - self::assertNotEmpty($response['Cart']['items']); - self::assertNotEmpty($response['Cart']['shipping_addresses']); + self::assertArrayHasKey('cart', $response); + self::assertNotEmpty($response['cart']['items']); + self::assertNotEmpty($response['cart']['shipping_addresses']); } /** @@ -110,7 +110,7 @@ public function testGetCartForGuest() $response = $this->graphQlQuery($query); - self::assertArrayHasKey('Cart', $response); + self::assertArrayHasKey('cart', $response); } public function testGetNonExistentCart() @@ -134,7 +134,7 @@ private function prepareGetCartQuery( ) : string { return <<<QUERY { - Cart(cart_id: "$maskedQuoteId") { + cart(cart_id: "$maskedQuoteId") { applied_coupon { code } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php index 62fae71fa79f5..74f53d4241496 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php @@ -10,7 +10,7 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Multishipping\Helper\Data; -use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; @@ -28,34 +28,35 @@ class SetBillingAddressOnCartTest extends GraphQlAbstract private $quoteResource; /** - * @var Quote + * @var QuoteFactory */ - private $quote; + private $quoteFactory; /** * @var QuoteIdToMaskedQuoteIdInterface */ private $quoteIdToMaskedId; + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->create(QuoteResource::class); - $this->quote = $objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ - public function testSetNewGuestBillingAddressOnCart() + public function testSetNewBillingAddressByGuest() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -104,14 +105,9 @@ public function testSetNewGuestBillingAddressOnCart() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ - public function testSetNewGuestBillingAddressOnUseForShippingCart() + public function testSetNewBillingAddressWithUseForShippingParameterByGuest() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -172,15 +168,12 @@ public function testSetNewGuestBillingAddressOnUseForShippingCart() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. */ - public function testSetSavedBillingAddressOnCartByGuest() + public function testSetBillingAddressFromAddressBookByGuest() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -194,19 +187,12 @@ public function testSetSavedBillingAddressOnCartByGuest() ) { cart { billing_address { - firstname - lastname - company - street city - postcode - telephone } } } } QUERY; - self::expectExceptionMessage('The current customer isn\'t authorized.'); $this->graphQlQuery($query); } @@ -214,23 +200,9 @@ public function testSetSavedBillingAddressOnCartByGuest() * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php */ - public function testSetNewRegisteredCustomerBillingAddressOnCart() + public function testSetNewBillingAddressByRegisteredCustomer() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $this->quote->setCustomerId(1); - $this->quoteResource->save($this->quote); - - $headerMap = $this->getHeaderMap(); + $maskedQuoteId = $this->assignQuoteToCustomer(); $query = <<<QUERY mutation { @@ -267,7 +239,7 @@ public function testSetNewRegisteredCustomerBillingAddressOnCart() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $headerMap); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -281,23 +253,9 @@ public function testSetNewRegisteredCustomerBillingAddressOnCart() * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php */ - public function testSetSavedRegisteredCustomerBillingAddressOnCart() + public function testSetBillingAddressFromAddressBookByRegisteredCustomer() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $this->quote->setCustomerId(1); - $this->quoteResource->save($this->quote); - - $headerMap = $this->getHeaderMap(); + $maskedQuoteId = $this->assignQuoteToCustomer(); $query = <<<QUERY mutation { @@ -323,7 +281,7 @@ public function testSetSavedRegisteredCustomerBillingAddressOnCart() } } QUERY; - $response = $this->graphQlQuery($query, [], '', $headerMap); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -332,6 +290,115 @@ public function testSetSavedRegisteredCustomerBillingAddressOnCart() $this->assertSavedBillingAddressFields($billingAddressResponse); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage Could not find a address with ID "100" + */ + public function testSetNotExistedBillingAddressFromAddressBook() + { + $maskedQuoteId = $this->assignQuoteToCustomer(); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + customer_address_id: 100 + } + } + ) { + cart { + billing_address { + city + } + } + } +} +QUERY; + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + */ + public function testSetNewBillingAddressAndFromAddressBookAtSameTime() + { + $maskedQuoteId = $this->assignQuoteToCustomer(); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + customer_address_id: 1 + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + } + ) { + cart { + billing_address { + city + } + } + } +} +QUERY; + + self::expectExceptionMessage( + 'The billing address cannot contain "customer_address_id" and "address" at the same time.' + ); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/three_customers.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The current user cannot use address with ID "1" + */ + public function testSetBillingAddressIfCustomerIsNotOwnerOfAddress() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + customer_address_id: 1 + } + } + ) { + cart { + billing_address { + city + } + } + } +} +QUERY; + $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); + } + /** * Verify the all the whitelisted fields for a New Address Object * @@ -374,19 +441,48 @@ private function assertSavedBillingAddressFields(array $billingAddressResponse): /** * @param string $username + * @param string $password * @return array + * @throws \Magento\Framework\Exception\AuthenticationException */ - private function getHeaderMap(string $username = 'customer@example.com'): array + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array { - $password = 'password'; - /** @var CustomerTokenServiceInterface $customerTokenService */ - $customerTokenService = ObjectManager::getInstance() - ->get(CustomerTokenServiceInterface::class); - $customerToken = $customerTokenService->createCustomerAccessToken($username, $password); + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; return $headerMap; } + /** + * @param string $reversedQuoteId + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } + + /** + * @param string $reversedQuoteId + * @param int $customerId + * @return string + * @throws \Magento\Framework\Exception\AlreadyExistsException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function assignQuoteToCustomer( + string $reversedQuoteId = 'test_order_with_simple_product_without_address', + int $customerId = 1 + ): string { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $quote->setCustomerId($customerId); + $this->quoteResource->save($quote); + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } + public function tearDown() { /** @var \Magento\Config\Model\ResourceModel\Config $config */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php index d60876e7c0be4..11fd37b94d535 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -10,7 +10,7 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Multishipping\Helper\Data; -use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; @@ -28,34 +28,35 @@ class SetShippingAddressOnCartTest extends GraphQlAbstract private $quoteResource; /** - * @var Quote + * @var QuoteFactory */ - private $quote; + private $quoteFactory; /** * @var QuoteIdToMaskedQuoteIdInterface */ private $quoteIdToMaskedId; + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->create(QuoteResource::class); - $this->quote = $objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ - public function testSetNewGuestShippingAddressOnCart() + public function testSetNewShippingAddressByGuest() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -105,15 +106,12 @@ public function testSetNewGuestShippingAddressOnCart() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. */ - public function testSetSavedShippingAddressOnCartByGuest() + public function testSetShippingAddressFromAddressBookByGuest() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -129,33 +127,22 @@ public function testSetSavedShippingAddressOnCartByGuest() ) { cart { shipping_addresses { - firstname - lastname - company - street city - postcode - telephone } } } } QUERY; - self::expectExceptionMessage('The current customer isn\'t authorized.'); $this->graphQlQuery($query); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php */ - public function testSetMultipleShippingAddressesOnCartByGuest() + public function testSetNewShippingAddressByRegisteredCustomer() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $maskedQuoteId = $this->assignQuoteToCustomer(); $query = <<<QUERY mutation { @@ -164,10 +151,18 @@ public function testSetMultipleShippingAddressesOnCartByGuest() cart_id: "$maskedQuoteId" shipping_addresses: [ { - customer_address_id: 1 - }, - { - customer_address_id: 1 + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } } ] } @@ -186,33 +181,23 @@ public function testSetMultipleShippingAddressesOnCartByGuest() } } QUERY; - /** @var \Magento\Config\Model\ResourceModel\Config $config */ - $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); - $config->saveConfig( - Data::XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE, - null, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - /** @var \Magento\Framework\App\Config\ReinitableConfigInterface $config */ - $config = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class); - $config->reinit(); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - self::expectExceptionMessage('You cannot specify multiple shipping addresses.'); - $this->graphQlQuery($query); + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewShippingAddressFields($shippingAddressResponse); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php */ - public function testSetSavedAndNewShippingAddressOnCartAtTheSameTime() + public function testSetShippingAddressFromAddressBookByRegisteredCustomer() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $maskedQuoteId = $this->assignQuoteToCustomer(); $query = <<<QUERY mutation { @@ -221,19 +206,7 @@ public function testSetSavedAndNewShippingAddressOnCartAtTheSameTime() cart_id: "$maskedQuoteId" shipping_addresses: [ { - customer_address_id: 1, - address: { - firstname: "test firstname" - lastname: "test lastname" - company: "test company" - street: ["test street 1", "test street 2"] - city: "test city" - region: "test region" - postcode: "887766" - country_code: "US" - telephone: "88776655" - save_in_address_book: false - } + customer_address_id: 1 } ] } @@ -252,23 +225,56 @@ public function testSetSavedAndNewShippingAddressOnCartAtTheSameTime() } } QUERY; - self::expectExceptionMessage( - 'The shipping address cannot contain "customer_address_id" and "address" at the same time.' - ); - $this->graphQlQuery($query); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); + $cartResponse = $response['setShippingAddressesOnCart']['cart']; + self::assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertSavedShippingAddressFields($shippingAddressResponse); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage Could not find a address with ID "100" */ - public function testSetShippingAddressOnCartWithNoAddresses() + public function testSetNotExistedShippingAddressFromAddressBook() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $maskedQuoteId = $this->assignQuoteToCustomer(); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + customer_address_id: 100 + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The shipping address must contain either "customer_address_id" or "address". + */ + public function testSetShippingAddressWithoutAddresses() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -282,45 +288,70 @@ public function testSetShippingAddressOnCartWithNoAddresses() ) { cart { shipping_addresses { - firstname - lastname - company - street city - postcode - telephone } } } } QUERY; - self::expectExceptionMessage( - 'The shipping address must contain either "customer_address_id" or "address".' - ); $this->graphQlQuery($query); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php */ - public function testSetNewRegisteredCustomerShippingAddressOnCart() + public function testSetNewShippingAddressAndFromAddressBookAtSameTime() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' + $maskedQuoteId = $this->assignQuoteToCustomer(); + + $query = <<<QUERY +mutation { + setShippingAddressesOnCart( + input: { + cart_id: "$maskedQuoteId" + shipping_addresses: [ + { + customer_address_id: 1, + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + ] + } + ) { + cart { + shipping_addresses { + city + } + } + } +} +QUERY; + self::expectExceptionMessage( + 'The shipping address cannot contain "customer_address_id" and "address" at the same time.' ); - $this->quote->setCustomerId(1); - $this->quoteResource->save($this->quote); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } - $headerMap = $this->getHeaderMap(); + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage You cannot specify multiple shipping addresses. + */ + public function testSetMultipleNewShippingAddresses() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -341,55 +372,57 @@ public function testSetNewRegisteredCustomerShippingAddressOnCart() telephone: "88776655" save_in_address_book: false } + }, + { + address: { + firstname: "test firstname 2" + lastname: "test lastname 2" + company: "test company 2" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } } ] } ) { cart { shipping_addresses { - firstname - lastname - company - street city - postcode - telephone } } } } QUERY; - $response = $this->graphQlQuery($query, [], '', $headerMap); + /** @var \Magento\Config\Model\ResourceModel\Config $config */ + $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); + $config->saveConfig( + Data::XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE, + null, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + /** @var \Magento\Framework\App\Config\ReinitableConfigInterface $config */ + $config = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class); + $config->reinit(); - self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); - $cartResponse = $response['setShippingAddressesOnCart']['cart']; - self::assertArrayHasKey('shipping_addresses', $cartResponse); - $shippingAddressResponse = current($cartResponse['shipping_addresses']); - $this->assertNewShippingAddressFields($shippingAddressResponse); + $this->graphQlQuery($query); } /** + * @magentoApiDataFixture Magento/Customer/_files/three_customers.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @expectedException \Exception + * @expectedExceptionMessage The current user cannot use address with ID "1" */ - public function testSetSavedRegisteredCustomerShippingAddressOnCart() + public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $this->quote->setCustomerId(1); - $this->quoteResource->save($this->quote); - - $headerMap = $this->getHeaderMap(); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -405,25 +438,14 @@ public function testSetSavedRegisteredCustomerShippingAddressOnCart() ) { cart { shipping_addresses { - firstname - lastname - company - street - city postcode - telephone } } } } QUERY; - $response = $this->graphQlQuery($query, [], '', $headerMap); - self::assertArrayHasKey('cart', $response['setShippingAddressesOnCart']); - $cartResponse = $response['setShippingAddressesOnCart']['cart']; - self::assertArrayHasKey('shipping_addresses', $cartResponse); - $shippingAddressResponse = current($cartResponse['shipping_addresses']); - $this->assertSavedShippingAddressFields($shippingAddressResponse); + $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** @@ -468,19 +490,48 @@ private function assertSavedShippingAddressFields(array $shippingAddressResponse /** * @param string $username + * @param string $password * @return array + * @throws \Magento\Framework\Exception\AuthenticationException */ - private function getHeaderMap(string $username = 'customer@example.com'): array + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array { - $password = 'password'; - /** @var CustomerTokenServiceInterface $customerTokenService */ - $customerTokenService = ObjectManager::getInstance() - ->get(CustomerTokenServiceInterface::class); - $customerToken = $customerTokenService->createCustomerAccessToken($username, $password); + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; return $headerMap; } + /** + * @param string $reversedQuoteId + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } + + /** + * @param string $reversedQuoteId + * @param int $customerId + * @return string + * @throws \Magento\Framework\Exception\AlreadyExistsException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function assignQuoteToCustomer( + string $reversedQuoteId = 'test_order_with_simple_product_without_address', + int $customerId = 1 + ): string { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $quote->setCustomerId($customerId); + $this->quoteResource->save($quote); + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } + public function tearDown() { /** @var \Magento\Config\Model\ResourceModel\Config $config */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php deleted file mode 100644 index 1c6679ee30f29..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingMethodOnCartTest.php +++ /dev/null @@ -1,252 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\Quote; - -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Quote\Model\Quote; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\GraphQlAbstract; - -/** - * Test for setting shipping methods on cart - */ -class SetShippingMethodOnCartTest extends GraphQlAbstract -{ - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var Quote - */ - private $quote; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->create(QuoteResource::class); - $this->quote = $objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetShippingMethodOnCart() - { - $shippingCarrierCode = 'flatrate'; - $shippingMethodCode = 'flatrate'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $shippingAddress = $this->quote->getShippingAddress(); - $shippingAddressId = $shippingAddress->getId(); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - $response = $this->sendRequestWithToken($query); - - self::assertArrayHasKey('setShippingMethodsOnCart', $response); - self::assertArrayHasKey('cart', $response['setShippingMethodsOnCart']); - self::assertEquals($maskedQuoteId, $response['setShippingMethodsOnCart']['cart']['cart_id']); - $addressesInformation = $response['setShippingMethodsOnCart']['cart']['shipping_addresses']; - self::assertCount(1, $addressesInformation); - self::assertEquals( - $addressesInformation[0]['selected_shipping_method']['code'], - $shippingCarrierCode . '_' . $shippingMethodCode - ); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetShippingMethodWithWrongCartId() - { - $shippingCarrierCode = 'flatrate'; - $shippingMethodCode = 'flatrate'; - $shippingAddressId = '1'; - $maskedQuoteId = 'invalid'; - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - self::expectExceptionMessage("Could not find a cart with ID \"$maskedQuoteId\""); - $this->sendRequestWithToken($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetNonExistingShippingMethod() - { - $shippingCarrierCode = 'non'; - $shippingMethodCode = 'existing'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $shippingAddress = $this->quote->getShippingAddress(); - $shippingAddressId = $shippingAddress->getId(); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - self::expectExceptionMessage("Carrier with such method not found: $shippingCarrierCode, $shippingMethodCode"); - $this->sendRequestWithToken($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetShippingMethodWithNonExistingAddress() - { - $shippingCarrierCode = 'flatrate'; - $shippingMethodCode = 'flatrate'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $shippingAddressId = '-20'; - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - self::expectExceptionMessage('The shipping address is missing. Set the address and try again.'); - $this->sendRequestWithToken($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetShippingMethodByGuestToCustomerCart() - { - $shippingCarrierCode = 'flatrate'; - $shippingMethodCode = 'flatrate'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $shippingAddress = $this->quote->getShippingAddress(); - $shippingAddressId = $shippingAddress->getId(); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $shippingMethodCode, - $shippingCarrierCode, - $shippingAddressId - ); - - self::expectExceptionMessage( - "The current user cannot perform operations on cart \"$maskedQuoteId\"" - ); - - $this->graphQlQuery($query); - } - - /** - * Generates query for setting the specified shipping method on cart - * - * @param string $maskedQuoteId - * @param string $shippingMethodCode - * @param string $shippingCarrierCode - * @param string $shippingAddressId - * @return string - */ - private function prepareMutationQuery( - string $maskedQuoteId, - string $shippingMethodCode, - string $shippingCarrierCode, - string $shippingAddressId - ) : string { - return <<<QUERY -mutation { - setShippingMethodsOnCart(input: - { - cart_id: "$maskedQuoteId", - shipping_methods: [ - { - shipping_method_code: "$shippingMethodCode" - shipping_carrier_code: "$shippingCarrierCode" - cart_address_id: $shippingAddressId - } - ]}) { - - cart { - cart_id, - shipping_addresses { - selected_shipping_method { - code - label - } - } - } - } -} - -QUERY; - } - - /** - * Sends a GraphQL request with using a bearer token - * - * @param string $query - * @return array - * @throws \Magento\Framework\Exception\AuthenticationException - */ - private function sendRequestWithToken(string $query): array - { - - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - - return $this->graphQlQuery($query, [], '', $headerMap); - } -} From 6478f84571442e0509de48b8599b52a6efcb3fa7 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Fri, 15 Feb 2019 18:49:26 -0600 Subject: [PATCH 098/254] ENGCOM-4289: GraphQL 2.3.1 release fixes --- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 5 +++++ .../Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php | 4 ---- .../Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php | 4 ---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 17dd1141a0368..a76f7d59c3f77 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -225,3 +225,8 @@ type CartItemSelectedOptionValuePrice { units: String! type: PriceTypeEnum! } + +input CartItemDetailsInput { + sku: String! + qty: Float! +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php index 74f53d4241496..bfe57109d107d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php @@ -443,7 +443,6 @@ private function assertSavedBillingAddressFields(array $billingAddressResponse): * @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 { @@ -455,7 +454,6 @@ private function getHeaderMap(string $username = 'customer@example.com', string /** * @param string $reversedQuoteId * @return string - * @throws \Magento\Framework\Exception\NoSuchEntityException */ private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string { @@ -469,8 +467,6 @@ private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): str * @param string $reversedQuoteId * @param int $customerId * @return string - * @throws \Magento\Framework\Exception\AlreadyExistsException - * @throws \Magento\Framework\Exception\NoSuchEntityException */ private function assignQuoteToCustomer( string $reversedQuoteId = 'test_order_with_simple_product_without_address', diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php index 11fd37b94d535..c3685c88ae487 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -492,7 +492,6 @@ private function assertSavedShippingAddressFields(array $shippingAddressResponse * @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 { @@ -504,7 +503,6 @@ private function getHeaderMap(string $username = 'customer@example.com', string /** * @param string $reversedQuoteId * @return string - * @throws \Magento\Framework\Exception\NoSuchEntityException */ private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string { @@ -518,8 +516,6 @@ private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): str * @param string $reversedQuoteId * @param int $customerId * @return string - * @throws \Magento\Framework\Exception\AlreadyExistsException - * @throws \Magento\Framework\Exception\NoSuchEntityException */ private function assignQuoteToCustomer( string $reversedQuoteId = 'test_order_with_simple_product_without_address', From f665ccaca268aab903bf37787fb0ccde1bae91c7 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 18 Feb 2019 10:45:50 +0200 Subject: [PATCH 099/254] MAGETWO-98013: [2.3] Configurable product is displayed as In Stock in product grid when it's set to out of stock --- app/code/Magento/Catalog/etc/adminhtml/di.xml | 1 - .../AddIsInStockFieldToCollection.php} | 6 +- .../CatalogInventory/etc/adminhtml/di.xml | 1 + .../Product/QuantityAndStockStatusTest.php | 74 ++++--------------- ...nd_stock_status_attribute_used_in_grid.php | 18 +++++ ...status_attribute_used_in_grid_rollback.php | 18 +++++ 6 files changed, 56 insertions(+), 62 deletions(-) rename app/code/Magento/{Catalog/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php => CatalogInventory/Ui/DataProvider/Product/AddIsInStockFieldToCollection.php} (75%) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid_rollback.php diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index c2028b921efb8..c04cfb2dce00a 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -87,7 +87,6 @@ <arguments> <argument name="addFieldStrategies" xsi:type="array"> <item name="websites" xsi:type="object">Magento\Catalog\Ui\DataProvider\Product\AddWebsitesFieldToCollection</item> - <item name="quantity_and_stock_status" xsi:type="object">Magento\Catalog\Ui\DataProvider\Product\AddQuantityAndStockStatusFieldToCollection</item> </argument> <argument name="addFilterStrategies" xsi:type="array"> <item name="store_id" xsi:type="object">Magento\Catalog\Ui\DataProvider\Product\AddStoreFieldToCollection</item> diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddIsInStockFieldToCollection.php similarity index 75% rename from app/code/Magento/Catalog/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php rename to app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddIsInStockFieldToCollection.php index cafe175a27e13..5be5409c90033 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php +++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddIsInStockFieldToCollection.php @@ -5,15 +5,15 @@ */ declare(strict_types=1); -namespace Magento\Catalog\Ui\DataProvider\Product; +namespace Magento\CatalogInventory\Ui\DataProvider\Product; use Magento\Framework\Data\Collection; use Magento\Ui\DataProvider\AddFieldToCollectionInterface; /** - * Class AddQuantityAndStockStatusFieldToCollection + * Add is_in_stock field to collection */ -class AddQuantityAndStockStatusFieldToCollection implements AddFieldToCollectionInterface +class AddIsInStockFieldToCollection implements AddFieldToCollectionInterface { /** * @inheritdoc diff --git a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml index 3397ef25918cd..c2acd1ce1f945 100644 --- a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml +++ b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml @@ -23,6 +23,7 @@ <arguments> <argument name="addFieldStrategies" xsi:type="array"> <item name="qty" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection</item> + <item name="quantity_and_stock_status" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddIsInStockFieldToCollection</item> </argument> <argument name="addFilterStrategies" xsi:type="array"> <item name="qty" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFilterToCollection</item> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php index 150c3f64e6c1c..98c5bbbd43d75 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php @@ -7,16 +7,13 @@ namespace Magento\Catalog\Ui\DataProvider\Product; -use Magento\Eav\Setup\EavSetup; +use Magento\CatalogInventory\Model\Stock\StockItemRepository; +use Magento\CatalogInventory\Ui\DataProvider\Product\AddIsInStockFieldToCollection; use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; -use Magento\Catalog\Model\Product; -use Magento\CatalogInventory\Model\Stock\StockItemRepository; -use Magento\CatalogInventory\Api\StockItemCriteriaInterface; -use Magento\Catalog\Api\Data\EavAttributeInterface; /** - * Class QuantityAndStockStatusTest + * Quantity and stock status test */ class QuantityAndStockStatusTest extends TestCase { @@ -30,53 +27,33 @@ class QuantityAndStockStatusTest extends TestCase */ private $objectManager; - /** - * @var int - */ - private $isUsedInGridValue; - /** * @inheritdoc */ protected function setUp() { $this->objectManager = Bootstrap::getObjectManager(); - $eavSetup = $this->objectManager->create(EavSetup::class); - $this->isUsedInGridValue = $eavSetup->getAttribute( - Product::ENTITY, - self::$quantityAndStockStatus, - EavAttributeInterface::IS_USED_IN_GRID - ); - $eavSetup->addAttribute( - Product::ENTITY, - self::$quantityAndStockStatus, - [ - EavAttributeInterface::IS_USED_IN_GRID => 1, - ] - ); } /** * Test product stock status in the products grid column * + * @magentoDataFixture Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php * @magentoDataFixture Magento/Checkout/_files/simple_product.php */ public function testProductStockStatus() { - $stockItemRepository = $this->objectManager->create(StockItemRepository::class); - - /** @var StockItemCriteriaInterface $stockItemCriteria */ - $stockItemCriteria = $this->objectManager->create(StockItemCriteriaInterface::class); - - $savedStockItem = current($stockItemRepository->getList($stockItemCriteria) - ->getItems()); - $savedStockItemId = $savedStockItem->getItemId(); + $productId = 1; - $savedStockItem->setIsInStock(true); - $savedStockItem->save(); + /** @var StockItemRepository $stockItemRepository */ + $stockItemRepository = $this->objectManager + ->create(StockItemRepository::class); + $stockItem = $stockItemRepository->get($productId); + $stockItem->setIsInStock(false); + $stockItemRepository->save($stockItem); - $savedStockItem->setIsInStock(false); - $savedStockItem->save(); + $savedStockItem = $stockItemRepository->get($productId); + $savedStockStatus = $savedStockItem->getData('is_in_stock'); $dataProvider = $this->objectManager->create( ProductDataProvider::class, @@ -86,34 +63,15 @@ public function testProductStockStatus() 'requestFieldName' => 'id', 'addFieldStrategies' => [ 'quantity_and_stock_status' => - $this->objectManager->get(AddQuantityAndStockStatusFieldToCollection::class) + $this->objectManager->get(AddIsInStockFieldToCollection::class) ] ] ); $dataProvider->addField(self::$quantityAndStockStatus); $data = $dataProvider->getData(); + $dataProviderStockStatus = $data['items'][0][self::$quantityAndStockStatus]; - $this->assertEquals( - $data['items'][0][self::$quantityAndStockStatus], - $savedStockItem->load($savedStockItemId) - ->getData('is_in_stock') - ); - } - - /** - * @inheritdoc - */ - protected function tearDown() - { - $eavSetup = $this->objectManager->create(EavSetup::class); - $eavSetup->addAttribute( - Product::ENTITY, - self::$quantityAndStockStatus, - [ - EavAttributeInterface::IS_USED_IN_GRID => $this->isUsedInGridValue, - ] - ); - parent::tearDown(); + $this->assertEquals($dataProviderStockStatus, $savedStockStatus); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php new file mode 100644 index 0000000000000..1870eaba566d8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$eavSetupFactory = $objectManager->create(\Magento\Eav\Setup\EavSetupFactory::class); +/** @var \Magento\Eav\Setup\EavSetup $eavSetup */ +$eavSetup = $eavSetupFactory->create(); +$eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'quantity_and_stock_status', + [ + 'is_used_in_grid' => 1, + ] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid_rollback.php new file mode 100644 index 0000000000000..fba12f19fdca8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid_rollback.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$eavSetupFactory = $objectManager->create(\Magento\Eav\Setup\EavSetupFactory::class); +/** @var \Magento\Eav\Setup\EavSetup $eavSetup */ +$eavSetup = $eavSetupFactory->create(); +$eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'quantity_and_stock_status', + [ + 'is_used_in_grid' => 0, + ] +); From 8d209466411884902a7f5d7804a1e55703f25462 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 18 Feb 2019 13:52:39 +0200 Subject: [PATCH 100/254] MAGETWO-98013: [2.3] Configurable product is displayed as In Stock in product grid when it's set to out of stock --- .../Product/QuantityAndStockStatusTest.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php index 98c5bbbd43d75..7f70b99cc002b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Ui\DataProvider\Product; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\CatalogInventory\Model\Stock\StockItemRepository; use Magento\CatalogInventory\Ui\DataProvider\Product\AddIsInStockFieldToCollection; use PHPUnit\Framework\TestCase; @@ -39,19 +40,21 @@ protected function setUp() * Test product stock status in the products grid column * * @magentoDataFixture Magento/Catalog/_files/quantity_and_stock_status_attribute_used_in_grid.php - * @magentoDataFixture Magento/Checkout/_files/simple_product.php + * @magentoDataFixture Magento/Catalog/_files/products.php */ public function testProductStockStatus() { - $productId = 1; + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $product = $productRepository->get('simple'); + $productId = $product->getId(); /** @var StockItemRepository $stockItemRepository */ - $stockItemRepository = $this->objectManager - ->create(StockItemRepository::class); + $stockItemRepository = $this->objectManager->create(StockItemRepository::class); + $stockItem = $stockItemRepository->get($productId); $stockItem->setIsInStock(false); $stockItemRepository->save($stockItem); - $savedStockItem = $stockItemRepository->get($productId); $savedStockStatus = $savedStockItem->getData('is_in_stock'); From 96793feaf7d4e8b3524ae859cae55fec880e41ff Mon Sep 17 00:00:00 2001 From: Wojtek Naruniec <wojtek@mediotype.com> Date: Mon, 18 Feb 2019 13:16:20 +0100 Subject: [PATCH 101/254] Skip cart validation if empty cart action is executed --- .../view/frontend/web/js/action/update-shopping-cart.js | 5 +++++ 1 file changed, 5 insertions(+) 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 ce1527b3d72d6..9d79e34b10f17 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 @@ -35,6 +35,11 @@ define([ return true; } + var action = this.element.find('#update_cart_action_container').val(); + if (action === 'empty_cart') { + return true; + } + if (this.isValid()) { event.preventDefault(); this.validateItems(this.options.validationURL, this.element.serialize()); From 9d4f13cc80d711fd9ac279f18cc6aa5b3576b178 Mon Sep 17 00:00:00 2001 From: Pratik Oza <magepratik@gmail.com> Date: Mon, 18 Feb 2019 18:27:14 +0530 Subject: [PATCH 102/254] Fixed pagination drop-down size --- .../backend/web/css/source/actions/_actions-dropdown.less | 1 + 1 file changed, 1 insertion(+) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less index cd089232412dc..d1fe33c4fe77d 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/actions/_actions-dropdown.less @@ -234,6 +234,7 @@ border: 0; display: inline; margin: 0; + width: 6rem; body._keyfocus &:focus { box-shadow: none; From 1845bcf1710ec364f7da2215c80ee91abf2eb1d6 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Mon, 18 Feb 2019 16:29:22 +0200 Subject: [PATCH 103/254] MAGETWO-98013: [2.3] Configurable product is displayed as In Stock in product grid when it's set to out of stock --- ...antityAndStockStatusFieldToCollection.php} | 4 ++-- .../CatalogInventory/etc/adminhtml/di.xml | 2 +- .../Product/QuantityAndStockStatusTest.php | 19 +++++++++---------- 3 files changed, 12 insertions(+), 13 deletions(-) rename app/code/Magento/CatalogInventory/Ui/DataProvider/Product/{AddIsInStockFieldToCollection.php => AddQuantityAndStockStatusFieldToCollection.php} (82%) diff --git a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddIsInStockFieldToCollection.php b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php similarity index 82% rename from app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddIsInStockFieldToCollection.php rename to app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php index 5be5409c90033..d66a783c6720d 100644 --- a/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddIsInStockFieldToCollection.php +++ b/app/code/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityAndStockStatusFieldToCollection.php @@ -11,9 +11,9 @@ use Magento\Ui\DataProvider\AddFieldToCollectionInterface; /** - * Add is_in_stock field to collection + * Add quantity_and_stock_status field to collection */ -class AddIsInStockFieldToCollection implements AddFieldToCollectionInterface +class AddQuantityAndStockStatusFieldToCollection implements AddFieldToCollectionInterface { /** * @inheritdoc diff --git a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml index c2acd1ce1f945..28035de29bc2e 100644 --- a/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml +++ b/app/code/Magento/CatalogInventory/etc/adminhtml/di.xml @@ -23,7 +23,7 @@ <arguments> <argument name="addFieldStrategies" xsi:type="array"> <item name="qty" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection</item> - <item name="quantity_and_stock_status" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddIsInStockFieldToCollection</item> + <item name="quantity_and_stock_status" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityAndStockStatusFieldToCollection</item> </argument> <argument name="addFilterStrategies" xsi:type="array"> <item name="qty" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFilterToCollection</item> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php index 7f70b99cc002b..c09d68a66ee8e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/QuantityAndStockStatusTest.php @@ -9,9 +9,11 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\CatalogInventory\Model\Stock\StockItemRepository; -use Magento\CatalogInventory\Ui\DataProvider\Product\AddIsInStockFieldToCollection; +use Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityAndStockStatusFieldToCollection; use PHPUnit\Framework\TestCase; use Magento\TestFramework\Helper\Bootstrap; +use Magento\CatalogInventory\Api\StockItemCriteriaInterface; +use Magento\CatalogInventory\Api\StockRegistryInterface; /** * Quantity and stock status test @@ -44,19 +46,16 @@ protected function setUp() */ public function testProductStockStatus() { - /** @var ProductRepositoryInterface $productRepository */ - $productRepository = $this->objectManager->get(ProductRepositoryInterface::class); - $product = $productRepository->get('simple'); - $productId = $product->getId(); - /** @var StockItemRepository $stockItemRepository */ $stockItemRepository = $this->objectManager->create(StockItemRepository::class); - $stockItem = $stockItemRepository->get($productId); + /** @var StockRegistryInterface $stockRegistry */ + $stockRegistry = $this->objectManager->create(StockRegistryInterface::class); + + $stockItem = $stockRegistry->getStockItemBySku('simple'); $stockItem->setIsInStock(false); $stockItemRepository->save($stockItem); - $savedStockItem = $stockItemRepository->get($productId); - $savedStockStatus = $savedStockItem->getData('is_in_stock'); + $savedStockStatus = (int)$stockItem->getIsInStock(); $dataProvider = $this->objectManager->create( ProductDataProvider::class, @@ -66,7 +65,7 @@ public function testProductStockStatus() 'requestFieldName' => 'id', 'addFieldStrategies' => [ 'quantity_and_stock_status' => - $this->objectManager->get(AddIsInStockFieldToCollection::class) + $this->objectManager->get(AddQuantityAndStockStatusFieldToCollection::class) ] ] ); From 27fb560b4cb1198fe01453e359cf839c0300c771 Mon Sep 17 00:00:00 2001 From: Ilan Parmentier <ilan.parmentier@artbambou.com> Date: Mon, 18 Feb 2019 15:37:52 +0100 Subject: [PATCH 104/254] Misconfigured aria-labelledby for product tabs Good afternoon, I find out attribute aria-labelledby is misconfigured. It must be declared for the content of the tab and not the title, in particular, for a parent tag. Ilan PARMENTIER Source : https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-1/tabs.html#sc1_label --- .../view/frontend/templates/product/view/details.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml index af664051b1431..39603a5223406 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml @@ -22,7 +22,6 @@ $label = $block->getChildData($alias, 'title'); ?> <div class="data item title" - aria-labelledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title" data-role="collapsible" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>"> <a class="data switch" tabindex="-1" @@ -32,7 +31,8 @@ <?= /* @escapeNotVerified */ $label ?> </a> </div> - <div class="data item content" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content"> + <div class="data item content" + aria-labelledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content"> <?= /* @escapeNotVerified */ $html ?> </div> <?php endforeach;?> From 009af396d851db41b6a9678c0942c71e103e9b76 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 19 Feb 2019 10:53:45 +0200 Subject: [PATCH 105/254] MAGETWO-97968: Minicart isn't updated for disabled products --- app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js index 27d38697afe39..81659e23c463b 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js @@ -8,5 +8,8 @@ define([ ], function (customerData) { 'use strict'; - customerData.reload(['cart'], false); + var cartData = customerData.get('cart'); + if (cartData()['items'] && cartData()['items'].length !== 0) { + customerData.reload(['cart'], false); + } }); From ae27fa1c757969902f605cb75b76be9206bee12e Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 19 Feb 2019 12:17:26 +0200 Subject: [PATCH 106/254] MAGETWO-97968: Minicart isn't updated for disabled products --- .../Test/StorefrontGuestCheckoutDisabledProductTest.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml index 29715d073d01b..4ec608a18a686 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -75,8 +75,6 @@ </createData> </before> <after> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> - <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createSimpleProduct2" stepKey="deleteProduct2"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> @@ -84,6 +82,8 @@ <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> + <actionGroup ref="resetProductGridToDefaultView" stepKey="resetGridToDefaultKeywordSearch"/> </after> <!-- Step 1: Add simple product to shopping cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> @@ -108,8 +108,7 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <!-- Disabled child configurable product --> <click selector="{{AdminProductFormSection.enableProductAttributeLabel}}" stepKey="clickDisableProduct"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> - <waitForPageLoad stepKey="waitForProductPageSaved"/> + <actionGroup ref="saveProductForm" stepKey="clickSaveProduct"/> <!-- Disabled simple product from grid --> <actionGroup ref="ChangeStatusProductUsingProductGridActionGroup" stepKey="disabledProductFromGrid"> <argument name="product" value="$$createSimpleProduct$$"/> From b179e544a9c05eb0d5347028d4095755d019e304 Mon Sep 17 00:00:00 2001 From: Milind Singh <milind7@live.com> Date: Tue, 19 Feb 2019 15:53:22 +0530 Subject: [PATCH 107/254] Issue Fixed #21322 : Declarative schema: Omitting indexType throws exception --- .../Framework/Setup/Declaration/Schema/Dto/Factories/Index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Factories/Index.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Factories/Index.php index 715f98c4177c0..211d3885297ba 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Factories/Index.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Factories/Index.php @@ -19,7 +19,7 @@ class Index implements FactoryInterface /** * Default index type. */ - const DEFAULT_INDEX_TYPE = "BTREE"; + const DEFAULT_INDEX_TYPE = "btree"; /** * @var ObjectManagerInterface From a19d50c9fd8f00156cca4936472b98df70ddc1a9 Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Tue, 19 Feb 2019 11:53:23 +0100 Subject: [PATCH 108/254] Change comment to "database" --- setup/src/Magento/Setup/Validator/DbValidator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/src/Magento/Setup/Validator/DbValidator.php b/setup/src/Magento/Setup/Validator/DbValidator.php index 1eae39160ff8e..95a1fef395b13 100644 --- a/setup/src/Magento/Setup/Validator/DbValidator.php +++ b/setup/src/Magento/Setup/Validator/DbValidator.php @@ -166,7 +166,7 @@ private function checkDatabasePrivileges(\Magento\Framework\DB\Adapter\AdapterIn return true; } - // check table privileges + // check database privileges $schemaPrivilegesQuery = "SELECT PRIVILEGE_TYPE FROM SCHEMA_PRIVILEGES " . "WHERE '$dbName' LIKE TABLE_SCHEMA AND REPLACE(GRANTEE, '\'', '') = current_user()"; $grantInfo = $connection->query($schemaPrivilegesQuery)->fetchAll(\PDO::FETCH_NUM); @@ -175,7 +175,7 @@ private function checkDatabasePrivileges(\Magento\Framework\DB\Adapter\AdapterIn } $errorMessage = 'Database user does not have enough privileges. Please make sure ' - . implode(', ', $requiredPrivileges) . " privileges are granted to table '{$dbName}'."; + . implode(', ', $requiredPrivileges) . " privileges are granted to database '{$dbName}'."; throw new \Magento\Setup\Exception($errorMessage); } From 43d82d0c1beb89aa12a782c89b0e2e79dc282dba Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 19 Feb 2019 13:43:15 +0200 Subject: [PATCH 109/254] MAGETWO-97968: Minicart isn't updated for disabled products --- app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js index 81659e23c463b..33c453b063ff6 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js @@ -9,6 +9,7 @@ define([ 'use strict'; var cartData = customerData.get('cart'); + if (cartData()['items'] && cartData()['items'].length !== 0) { customerData.reload(['cart'], false); } From 5c67e67a796861e37d6abe07dc42ca1ea8763202 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 19 Feb 2019 13:46:50 +0200 Subject: [PATCH 110/254] Fix static test. --- .../module/checkout/_order-summary.less | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less index ae21a3877c350..beb8fc8d4c3b9 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_order-summary.less @@ -233,21 +233,21 @@ // _____________________________________________ @media only screen and (max-width: @screen__m) { - .opc-block-summary { - .product-item { - .product-item-inner { - display: block; - } - - .product-item-name-block { - display: block; - text-align: left; - } - - .subtotal { - display: block; - text-align: left; - } + .opc-block-summary { + .product-item { + .product-item-inner { + display: block; + } + + .product-item-name-block { + display: block; + text-align: left; + } + + .subtotal { + display: block; + text-align: left; + } + } } - } -} \ No newline at end of file +} From 7b9e807c8966308d173216d62f7ab40b11725353 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Fri, 25 Jan 2019 13:22:21 +0000 Subject: [PATCH 111/254] Changed references to "Store" to "Scope" in framework components. --- .../Sales/Model/Order/Email/SenderBuilder.php | 2 +- .../Model/Order/Email/SenderBuilderTest.php | 8 +++--- .../Mail/Template/TransportBuilder.php | 26 ++++++++++++++----- .../Unit/Template/TransportBuilderTest.php | 8 +++--- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index a7d749ec04c7d..ed9e38822245f 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -106,7 +106,7 @@ protected function configureEmailTemplate() $this->transportBuilder->setTemplateIdentifier($this->templateContainer->getTemplateId()); $this->transportBuilder->setTemplateOptions($this->templateContainer->getTemplateOptions()); $this->transportBuilder->setTemplateVars($this->templateContainer->getTemplateVars()); - $this->transportBuilder->setFromByStore( + $this->transportBuilder->setFromByScope( $this->identityContainer->getEmailIdentity(), $this->identityContainer->getStore()->getId() ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php index 759d60d9e6613..24cd54e3a46b3 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php @@ -76,7 +76,7 @@ protected function setUp() 'setTemplateIdentifier', 'setTemplateOptions', 'setTemplateVars', - 'setFromByStore', + 'setFromByScope', ] ); @@ -103,7 +103,7 @@ protected function setUp() ->method('getEmailIdentity') ->will($this->returnValue($emailIdentity)); $this->transportBuilder->expects($this->once()) - ->method('setFromByStore') + ->method('setFromByScope') ->with($this->equalTo($emailIdentity), 1); $this->identityContainerMock->expects($this->once()) @@ -146,7 +146,7 @@ public function testSend() ->method('getId') ->willReturn(1); $this->transportBuilder->expects($this->once()) - ->method('setFromByStore') + ->method('setFromByScope') ->with($identity, 1); $this->transportBuilder->expects($this->once()) ->method('addTo') @@ -176,7 +176,7 @@ public function testSendCopyTo() ->method('addTo') ->with($this->equalTo('example@mail.com')); $this->transportBuilder->expects($this->once()) - ->method('setFromByStore') + ->method('setFromByScope') ->with($identity, 1); $this->identityContainerMock->expects($this->once()) ->method('getStore') diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index a7bb96122a84d..0d69b3d96cebf 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -177,29 +177,43 @@ public function setReplyTo($email, $name = null) /** * Set mail from address * - * @deprecated This function sets the from address for the first store only. - * new function setFromByStore introduced to allow setting of from address - * based on store. - * @see setFromByStore() + * @deprecated This function sets the from address but does not provide + * a way of setting the correct from addresses based on the scope. + * @see setFromByScope() * * @param string|array $from * @return $this */ public function setFrom($from) { - return $this->setFromByStore($from, null); + return $this->setFromByScope($from, null); } /** * Set mail from address by store * + * @deprecated Use setFromByScope + * @see setFromByScope() + * * @param string|array $from * @param string|int $store * @return $this */ public function setFromByStore($from, $store = null) { - $result = $this->_senderResolver->resolve($from, $store); + return $this->setFromByScope($from, $store); + } + + /** + * Set mail from address by scopeId + * + * @param string|array $from + * @param string|int $scopeId + * @return $this + */ + public function setFromByScope($from, $scopeId = null) + { + $result = $this->_senderResolver->resolve($from, $scopeId); $this->message->setFromAddress($result['email'], $result['name']); return $this; } diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index b476eecd7f59f..5e3309af6497b 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -167,20 +167,20 @@ public function getTransportDataProvider() /** * @return void */ - public function testSetFromByStore() + public function testSetFromByScope() { $sender = ['email' => 'from@example.com', 'name' => 'name']; - $store = 1; + $scopeId = 1; $this->senderResolverMock->expects($this->once()) ->method('resolve') - ->with($sender, $store) + ->with($sender, $scopeId) ->willReturn($sender); $this->messageMock->expects($this->once()) ->method('setFromAddress') ->with($sender['email'], $sender['name']) ->willReturnSelf(); - $this->builder->setFromByStore($sender, $store); + $this->builder->setFromByScope($sender, $scopeId); } /** From f98c124e547f528e99e6a14e650c5c171ef963e6 Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Tue, 19 Feb 2019 14:48:20 +0200 Subject: [PATCH 112/254] MAGETWO-97968: Minicart isn't updated for disabled products --- app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js index 33c453b063ff6..4b30ad8075274 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/empty-cart.js @@ -10,7 +10,7 @@ define([ var cartData = customerData.get('cart'); - if (cartData()['items'] && cartData()['items'].length !== 0) { + if (cartData().items && cartData().items.length !== 0) { customerData.reload(['cart'], false); } }); From 3e832846dac8640fed86f8e0f0984f027c2f8b04 Mon Sep 17 00:00:00 2001 From: Shikha Mishra <shikhamishra@cedcoss.com> Date: Tue, 19 Feb 2019 20:00:48 +0530 Subject: [PATCH 113/254] Fixed #15059 Cannot reorder from the first try --- app/code/Magento/Sales/Model/AdminOrder/Create.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 088ad5a61f6c3..1ba5984799414 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -582,6 +582,7 @@ public function initFromOrder(\Magento\Sales\Model\Order $order) } $quote->getShippingAddress()->unsCachedItemsAll(); + $quote->getBillingAddress()->unsCachedItemsAll(); $quote->setTotalsCollectedFlag(false); $this->quoteRepository->save($quote); From 2d4f079ecfefc9e1775239539ad5d9408bc9868e Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 19 Feb 2019 10:38:25 -0600 Subject: [PATCH 114/254] MAGETWO-98258: Fix elacticsearch search perfomance - fix static --- .../SearchAdapter/Aggregation/Builder/Term.php | 5 ++++- .../SearchAdapter/Query/Builder/Match.php | 11 ++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php index bcfb7f5565b86..0c03a9df18dc8 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Aggregation/Builder/Term.php @@ -8,10 +8,13 @@ use Magento\Framework\Search\Request\BucketInterface as RequestBucketInterface; use Magento\Framework\Search\Dynamic\DataProviderInterface; +/** + * Builder for term buckets. + */ class Term implements BucketBuilderInterface { /** - * {@inheritdoc} + * @inheritdoc */ public function build( RequestBucketInterface $bucket, diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index f1c3451482bab..149405aff3e78 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -10,6 +10,9 @@ use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface; use Magento\Framework\Search\Adapter\Preprocessor\PreprocessorInterface; +/** + * Builder for match query. + */ class Match implements QueryInterface { /** @@ -40,7 +43,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $conditionType) { @@ -61,6 +64,8 @@ public function build(array $selectQuery, RequestQueryInterface $requestQuery, $ } /** + * Prepare query. + * * @param string $queryValue * @param string $conditionType * @return array @@ -124,11 +129,11 @@ protected function buildQueries(array $matches, array $queryValue) } /** + * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. + * * Cut trailing plus or minus sign, and @ symbol, using of which causes InnoDB to report a syntax error. * @link https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs. * - * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. - * * @param string $value * @return string */ From 2a884e574241b4ab1d8093e1421cf980283383c7 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Tue, 19 Feb 2019 16:44:12 +0000 Subject: [PATCH 115/254] magento/magento2#20621: Removed deprecated method --- .../Mail/Template/TransportBuilder.php | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 0d69b3d96cebf..b9271c0209fd3 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -183,33 +183,20 @@ public function setReplyTo($email, $name = null) * * @param string|array $from * @return $this + * @throws \Magento\Framework\Exception\MailException */ public function setFrom($from) { return $this->setFromByScope($from, null); } - /** - * Set mail from address by store - * - * @deprecated Use setFromByScope - * @see setFromByScope() - * - * @param string|array $from - * @param string|int $store - * @return $this - */ - public function setFromByStore($from, $store = null) - { - return $this->setFromByScope($from, $store); - } - /** * Set mail from address by scopeId * * @param string|array $from * @param string|int $scopeId * @return $this + * @throws \Magento\Framework\Exception\MailException */ public function setFromByScope($from, $scopeId = null) { @@ -270,6 +257,7 @@ public function setTemplateOptions($templateOptions) * Get mail transport * * @return \Magento\Framework\Mail\TransportInterface + * @throws LocalizedException */ public function getTransport() { From d6a4106edf3b73781dc73716c046d2f414bf6472 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 19 Feb 2019 11:03:19 -0600 Subject: [PATCH 116/254] MC-4408: Convert UpdateSimpleProductEntityTest to MFTF - Use action group to filter product grid --- ...VerifyDataOverridingOnStoreViewLevelTest.xml | 11 ++++++----- ...VerifyDataOverridingOnStoreViewLevelTest.xml | 12 +++++++----- .../AdminUpdateSimpleProductTieredPriceTest.xml | 11 ++++++----- ...thRegularPriceInStockDisabledProductTest.xml | 10 +++++----- ...ctWithRegularPriceInStockEnabledFlatTest.xml | 10 +++++----- ...arPriceInStockNotVisibleIndividuallyTest.xml | 10 +++++----- ...ularPriceInStockUnassignFromCategoryTest.xml | 10 +++++----- ...riceInStockVisibleInCatalogAndSearchTest.xml | 10 +++++----- ...ularPriceInStockVisibleInCatalogOnlyTest.xml | 10 +++++----- ...gularPriceInStockVisibleInSearchOnlyTest.xml | 10 +++++----- ...RegularPriceInStockWithCustomOptionsTest.xml | 17 ++++++++++------- ...pleProductWithRegularPriceOutOfStockTest.xml | 10 +++++----- 12 files changed, 69 insertions(+), 62 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml index a5c2e1aa3a6de..18e4ff9ee2c99 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="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> @@ -44,10 +44,9 @@ <!-- Search default simple product in grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -57,10 +56,12 @@ <click selector="{{AdminProductFormChangeStoreSection.acceptButton}}" stepKey="clickAcceptButton"/> <waitForPageLoad stepKey="waitForThePageToLoad"/> <uncheckOption selector="{{AdminProductFormSection.productNameUseDefault}}" stepKey="uncheckProductStatus"/> + <!-- Update default simple product with name --> <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductDataOverriding.name}}" stepKey="fillSimpleProductName"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml index cd18110368919..d5fc981b5b2e6 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="LoginAsAdmin" stepKey="loginAsAdmin"/> <actionGroup ref="CreateStoreView" stepKey="createCustomStoreViewFr"> <argument name="storeView" value="customStoreFR"/> </actionGroup> @@ -44,21 +44,23 @@ <!-- Search default simple product in grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + <!-- Assign simple product to created store view --> <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="clickCategoryStoreViewDropdownToggle"/> <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewOption(customStoreFR.name)}}" stepKey="selectCategoryStoreViewOption"/> <click selector="{{AdminProductFormChangeStoreSection.acceptButton}}" stepKey="clickAcceptButton"/> <waitForPageLoad stepKey="waitForPageToLoad"/> + <!-- Update default simple product with price --> <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductDataOverriding.price}}" stepKey="fillSimpleProductPrice"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml index e63b1fdfe31bc..2c3aa5db75171 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="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -38,10 +38,9 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -49,6 +48,7 @@ <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{simpleProductTierPrice300InStock.name}}" stepKey="fillSimpleProductName"/> <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{simpleProductTierPrice300InStock.sku}}" stepKey="fillSimpleProductSku"/> <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{simpleProductTierPrice300InStock.price}}" stepKey="fillSimpleProductPrice"/> + <!-- Press enter to validate advanced pricing link --> <pressKey selector="{{AdminProductFormSection.productPrice}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ENTER]" stepKey="pressEnterKey"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> @@ -74,6 +74,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml index 7c2f8b4c458cc..6e8f1ba6f12a6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockDisabledProductTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -36,10 +36,9 @@ <!-- Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -56,6 +55,7 @@ <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="clickEnableProductLabelToDisableProduct"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml index 8f032b93c37b5..a042c4d60ae4f 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="LoginAsAdmin" stepKey="loginAsAdmin"/> <magentoCLI stepKey="setFlatCatalogProduct" command="config:set catalog/frontend/flat_catalog_product 1"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> @@ -40,10 +40,9 @@ <!-- Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -73,6 +72,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml index c163e7bfa7ab9..d08ef9c93999c 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="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -38,10 +38,9 @@ <!-- Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -64,6 +63,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockUnassignFromCategoryTest.xml index 8476afe5263e6..3433a09117322 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="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="_defaultProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -36,10 +36,9 @@ <!--Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillDefaultSimpleProductName"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -51,6 +50,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml index a7c7b66171983..a695982921cfd 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -38,10 +38,9 @@ <!-- Search default simple product in the grid page --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -64,6 +63,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml index 675206283a4ef..ba52c6d2bc261 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -38,10 +38,9 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -64,6 +63,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml index 4b54b0e7c6fcf..cb5c24839e387 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="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -38,10 +38,9 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -64,6 +63,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml index 2ee993d2af696..c9a37ec40e8fa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -19,7 +19,7 @@ <group value="mtf_migrated"/> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -38,10 +38,9 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -61,6 +60,7 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPriceCustomOptions.urlKey}}" stepKey="fillUrlKey"/> <click selector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" stepKey="clickAdminProductCustomizableOption"/> + <!-- Create simple product with customizable option --> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOptionButton"/> <waitForPageLoad stepKey="waitForDataToLoad"/> @@ -76,10 +76,11 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> - <!--Verify customer see success message--> + + <!--Verify customer see success message--> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> - <!--Search updated simple product(from above step) in the grid page--> + <!--Search updated simple product(from above step) in the grid page--> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedSimpleProduct"/> <waitForPageLoad stepKey="waitForProductCatalogPageToLoad"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAll"/> @@ -103,6 +104,7 @@ <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{simpleProductRegularPriceCustomOptions.urlKey}}" stepKey="seeUrlKey"/> <click selector="{{AdminProductCustomizableOptionsSection.checkIfCustomizableOptionsTabOpen}}" stepKey="clickAdminProductCustomizableOptionToSeeValues"/> + <!-- Verify simple product with customizable options --> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOptionButtonForCustomizableOption"/> <waitForPageLoad stepKey="waitForPageLoad"/> @@ -134,6 +136,7 @@ <!--Verify customer see customizable options are Required --> <seeElement selector="{{StorefrontProductInfoMainSection.requiredCustomSelect(simpleProductCustomizableOption.title)}}" stepKey="verifyFistCustomOptionIsRequired"/> + <!--Verify customer see customizable option titles and prices --> <grabAttributeFrom userInput="for" selector="{{StorefrontProductInfoMainSection.customOptionLabel(simpleProductCustomizableOption.title)}}" stepKey="simpleOptionId"/> <grabMultiple selector="{{StorefrontProductInfoMainSection.customSelectOptions({$simpleOptionId})}}" stepKey="grabFourthOptions"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml index ea575bdb2771e..54ed753b80a1c 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="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultSimpleProduct" stepKey="initialSimpleProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> @@ -38,10 +38,9 @@ <!-- Search default simple product in the grid --> <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter"/> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialSimpleProduct.name$$" stepKey="fillSimpleProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> + <actionGroup ref="filterProductGridBySku2" stepKey="filterProductGrid"> + <argument name="sku" value="$$initialSimpleProduct.sku$$"/> + </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToOpenDefaultSimpleProduct"/> <waitForPageLoad stepKey="waitUntilProductIsOpened"/> @@ -63,6 +62,7 @@ <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> <waitForPageLoad stepKey="waitForSimpleProductSave"/> + <!-- Verify customer see success message --> <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> From 168f916d35011d5147a1b7c7218cd122724b91f4 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Tue, 19 Feb 2019 11:30:20 -0600 Subject: [PATCH 117/254] MAGETWO-98258: Fix elacticsearch search perfomance - fix static --- .../Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php index 149405aff3e78..aaa9d8a88382f 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Query/Builder/Match.php @@ -132,7 +132,7 @@ protected function buildQueries(array $matches, array $queryValue) * Escape a value for special query characters such as ':', '(', ')', '*', '?', etc. * * Cut trailing plus or minus sign, and @ symbol, using of which causes InnoDB to report a syntax error. - * @link https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs. + * https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html Fulltext-boolean search docs. * * @param string $value * @return string From fb18e4fc67782af1f9379bf5f1eeb8d4dd58c67b Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 19 Feb 2019 12:22:14 -0600 Subject: [PATCH 118/254] GraphQL-363: Always return cart id null after add product --- .../Resolver/AddSimpleProductsToCart.php | 1 - .../QuoteGraphQl/Model/Resolver/Cart.php | 10 +- .../Magento/GraphQl/Quote/GetCartTest.php | 124 ++++++++---------- 3 files changed, 57 insertions(+), 78 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php index ff0298416cdf8..f4335b262c854 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php @@ -81,7 +81,6 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $this->addProductsToCart->execute($cart, $cartItems); $cartData = $this->extractDataFromCart->execute($cart); - $cartData["cart_id"] = $cartHash; return [ 'cart' => $cartData, diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Cart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/Cart.php index 5023c186f1e6c..1849ba0803868 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/Cart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/Cart.php @@ -54,14 +54,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $currentUserId = $context->getUserId(); $cart = $this->getCartForUser->execute($maskedCartId, $currentUserId); - $data = array_merge( - [ - 'cart_id' => $maskedCartId, - 'model' => $cart - ], - $this->extractDataFromCart->execute($cart) - ); - + $data = $this->extractDataFromCart->execute($cart); + $data['model'] = $cart; return $data; } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php index 44cd2ef526bd5..6c5add7df6b0f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php @@ -8,7 +8,7 @@ namespace Magento\GraphQl\Quote; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; @@ -19,20 +19,15 @@ */ class GetCartTest extends GraphQlAbstract { - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - /** * @var QuoteResource */ private $quoteResource; /** - * @var Quote + * @var QuoteFactory */ - private $quote; + private $quoteFactory; /** * @var QuoteIdToMaskedQuoteIdInterface @@ -40,103 +35,87 @@ class GetCartTest extends GraphQlAbstract private $quoteIdToMaskedId; /** - * @inheritdoc + * @var CustomerTokenServiceInterface */ + private $customerTokenService; + protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->create(QuoteResource::class); - $this->quote = $objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php */ - public function testGetOwnCartForRegisteredCustomer() + public function testGetCartForGuest() { - $reservedOrderId = 'test_order_item_with_items'; - $this->quoteResource->load( - $this->quote, - $reservedOrderId, - 'reserved_order_id' - ); - - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = $this->prepareGetCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $query = $this->getCartQuery($maskedQuoteId); - $response = $this->sendRequestWithToken($query); + $response = $this->graphQlQuery($query); self::assertArrayHasKey('cart', $response); - self::assertNotEmpty($response['cart']['items']); - self::assertNotEmpty($response['cart']['shipping_addresses']); + self::assertEquals($maskedQuoteId, $response['cart']['cart_id']); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php */ - public function testGetCartFromAnotherCustomer() + public function testGetCartByRegisteredCustomer() { - $reservedOrderId = 'test_order_item_with_items'; - $this->quoteResource->load( - $this->quote, - $reservedOrderId, - 'reserved_order_id' - ); - - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = $this->prepareGetCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $query = $this->getCartQuery($maskedQuoteId); - self::expectExceptionMessage("The current user cannot perform operations on cart \"$maskedQuoteId\""); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - $this->graphQlQuery($query); + self::assertArrayHasKey('cart', $response); + self::assertEquals($maskedQuoteId, $response['cart']['cart_id']); + self::assertNotEmpty($response['cart']['items']); } /** - * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php */ - public function testGetCartForGuest() + public function testGetCartOfAnotherCustomerByGuest() { - $reservedOrderId = 'test_order_1'; - $this->quoteResource->load( - $this->quote, - $reservedOrderId, - 'reserved_order_id' - ); - - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = $this->prepareGetCartQuery($maskedQuoteId); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $query = $this->getCartQuery($maskedQuoteId); - $response = $this->graphQlQuery($query); - - self::assertArrayHasKey('cart', $response); + self:$this->expectExceptionMessage( + "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" + ); + $this->graphQlQuery($query); } + /** + * @expectedException \Exception + * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" + */ public function testGetNonExistentCart() { $maskedQuoteId = 'non_existent_masked_id'; - $query = $this->prepareGetCartQuery($maskedQuoteId); - - self::expectExceptionMessage("Could not find a cart with ID \"$maskedQuoteId\""); + $query = $this->getCartQuery($maskedQuoteId); $this->graphQlQuery($query); } /** - * Generates query for setting the specified shipping method on cart - * * @param string $maskedQuoteId * @return string */ - private function prepareGetCartQuery( + private function getCartQuery( string $maskedQuoteId ) : string { return <<<QUERY { cart(cart_id: "$maskedQuoteId") { + cart_id applied_coupon { - code + code } items { id @@ -147,23 +126,30 @@ private function prepareGetCartQuery( } } } - QUERY; } /** - * Sends a GraphQL request with using a bearer token - * - * @param string $query - * @return array - * @throws \Magento\Framework\Exception\AuthenticationException + * @param string $reversedQuoteId + * @return string */ - private function sendRequestWithToken(string $query): array + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } - return $this->graphQlQuery($query, [], '', $headerMap); + /** + * @param string $username + * @param string $password + * @return array + */ + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; } } From c84054f3a05b404d7ac7a1fd410f3cf39363b112 Mon Sep 17 00:00:00 2001 From: John S <john00ivy@gmail.com> Date: Tue, 19 Feb 2019 13:02:56 -0600 Subject: [PATCH 119/254] MC-4409: Convert UpdateProductAttributeEntityTest to MFTF - Adding "waitForPageLoad" action to the Action Group. - Adding clean up steps to the "after" block of the Test. - Fixing the name of the "createData" reference. There is a camel-casing issue with the original name. --- .../AdminProductAttributeSetActionGroup.xml | 1 + ...sibleInStorefrontAdvancedSearchFormTest.xml | 16 ++++++++++++++++ ...sibleInStorefrontAdvancedSearchFormTest.xml | 18 +++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml index 458fb9a509559..8a450a4733600 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeSetActionGroup.xml @@ -62,5 +62,6 @@ <fillField selector="{{AdminProductAttributeSetGridSection.filter}}" userInput="{{name}}" stepKey="filterByName"/> <click selector="{{AdminProductAttributeSetGridSection.searchBtn}}" stepKey="clickSearch"/> <click selector="{{AdminProductAttributeSetGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> + <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index c0d334861642d..19553f6893fcb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -5,6 +5,7 @@ * See COPYING.txt for license details. */ --> + <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest"> @@ -17,6 +18,7 @@ <severity value="CRITICAL"/> <group value="mtf_migrated"/> </annotations> + <before> <!-- Create product attribute with 2 options --> <createData entity="productDropDownAttributeNotSearchable" stepKey="attribute"/> @@ -26,17 +28,24 @@ <createData entity="productAttributeOption2" stepKey="option2"> <requiredEntity createDataKey="attribute"/> </createData> + <!-- Create product attribute set --> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Filter product attribute set by attribute set name --> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> <actionGroup ref="FilterProductAttributeSetGridByAttributeSetName" stepKey="filterProductAttrSetGridByAttrSetName"> <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> </actionGroup> + <!-- Assert created attribute in an unassigned attributes --> <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassignedAttr"/> + <!-- Assign attribute in the group --> <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> @@ -44,26 +53,33 @@ </actionGroup> <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttributeInGroup"/> <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> + <!-- Go to Product Attribute Grid page --> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="$$attribute.attribute_code$$" stepKey="fillAttrCodeField" /> <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearchBtn" /> <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="chooseFirstRow" /> + <!-- Change attribute property: Frontend Label --> <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{productDropDownAttribute.attribute_code}}" stepKey="fillDefaultLabel"/> + <!-- Change attribute property: Use in Search >Yes --> <scrollToTopOfPage stepKey="scrollToTabs"/> <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" stepKey="waitTabLoad"/> <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="Yes" stepKey="seeInSearch"/> + <!-- Change attribute property: Visible In Advanced Search >No --> <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" stepKey="waitTabLoad2"/> <selectOption selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" userInput="No" stepKey="dontSeeInAdvancedSearch"/> + <!-- Save the new product attributes --> <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSave"/> <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> + <!-- Flash cache --> <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!-- Go to store's advanced catalog search page --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> <dontSeeElement selector="{{StorefrontCatalogSearchAdvancedFormSection.AttributeByCode('$$attribute.attribute_code$$')}}" stepKey="dontSeeAttribute"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index b7fdb7bd67941..de373f246fa76 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -5,6 +5,7 @@ * See COPYING.txt for license details. */ --> + <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest"> @@ -17,26 +18,34 @@ <severity value="CRITICAL"/> <group value="mtf_migrated"/> </annotations> + <before> <!-- Create a multiple select product attribute with two options --> - <createData entity="productAttributeMultiSelectTwoOptionsNotSearchable" stepKey="attribute"/> + <createData entity="productAttributeMultiselectTwoOptionsNotSearchable" stepKey="attribute"/> <createData entity="productAttributeOption1" stepKey="option1"> <requiredEntity createDataKey="attribute"/> </createData> <createData entity="productAttributeOption2" stepKey="option2"> <requiredEntity createDataKey="attribute"/> </createData> + <!-- Create product attribute set --> <createData entity="CatalogAttributeSet" stepKey="createAttributeSet"/> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Filter product attribute set by attribute set name --> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> <actionGroup ref="FilterProductAttributeSetGridByAttributeSetName" stepKey="filterProductAttrSetGridByAttrSetName"> <argument name="name" value="$$createAttributeSet.attribute_set_name$$"/> </actionGroup> + <!-- Assert created attribute in an unassigned attributes --> <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.unassignedAttributesTree}}" stepKey="seeAttributeInUnassignedAttr"/> + <!-- Assign attribute in the group --> <actionGroup ref="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> <argument name="group" value="Product Details"/> @@ -44,26 +53,33 @@ </actionGroup> <see userInput="$$attribute.attribute_code$$" selector="{{AdminProductAttributeSetEditSection.groupTree}}" stepKey="seeAttributeInGroup"/> <actionGroup ref="SaveAttributeSet" stepKey="saveAttributeSet"/> + <!-- Go to Product Attribute Grid page --> <amOnPage url="{{AdminProductAttributeGridPage.url}}" stepKey="navigateToProductAttributeGrid"/> <fillField selector="{{AdminProductAttributeGridSection.FilterByAttributeCode}}" userInput="$$attribute.attribute_code$$" stepKey="fillAttrCodeField" /> <click selector="{{AdminProductAttributeGridSection.Search}}" stepKey="clickSearchBtn" /> <click selector="{{AdminProductAttributeGridSection.FirstRow}}" stepKey="chooseFirstRow" /> + <!-- Change attribute property: Frontend Label --> <fillField selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{productDropDownAttribute.attribute_code}}" stepKey="fillDefaultLabel"/> + <!-- Change attribute property: Use in Search >Yes --> <scrollToTopOfPage stepKey="scrollToTabs"/> <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" stepKey="waitTabLoad"/> <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="Yes" stepKey="seeInSearch"/> + <!-- Change attribute property: Visible In Advanced Search >No --> <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" stepKey="waitTabLoad2"/> <selectOption selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" userInput="No" stepKey="dontSeeInAdvancedSearch"/> + <!-- Save the new product attributes --> <click selector="{{AttributePropertiesSection.Save}}" stepKey="clickSave"/> <seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSuccessMessage"/> + <!-- Flash cache --> <magentoCLI command="cache:flush" stepKey="flushCache"/> + <!-- Go to store's advanced catalog search page --> <actionGroup ref="GoToStoreViewAdvancedCatalogSearchActionGroup" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> <dontSeeElement selector="{{StorefrontCatalogSearchAdvancedFormSection.AttributeByCode('$$attribute.attribute_code$$')}}" stepKey="dontSeeAttribute"/> From bce29bd32b411f8bae26032e2f4f8e1ea15bbfe1 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 19 Feb 2019 13:14:04 -0600 Subject: [PATCH 120/254] MC-4527: Convert VerifyConfigurableProductEntityPriceTest to MFTF - Make use of extends --- ...oductPriceWithDisabledChildProductTest.xml | 26 +++- ...uctPriceWithOutOfStockChildProductTest.xml | 142 +----------------- 2 files changed, 30 insertions(+), 138 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml index 0d83d59b4ce32..86978a4121a43 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml @@ -20,8 +20,10 @@ <before> <!-- Login as Admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <!-- Create Default Category --> <createData entity="_defaultCategory" stepKey="createCategory"/> + <!-- Create an attribute with three options to be used in the first child product --> <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> @@ -33,44 +35,53 @@ <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> <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> + <!-- Get the third option of the attribute created --> <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> <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"/> <field key="price">10.00</field> </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"/> <field key="price">20.00</field> </createData> + <!--Create a simple product and give it the attribute with the Third option --> <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct3"> - <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> <requiredEntity createDataKey="getConfigAttributeOption3"/> <field key="price">30.00</field> </createData> + <!-- Create the configurable product --> <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOption"> <requiredEntity createDataKey="createConfigProduct"/> @@ -79,16 +90,19 @@ <requiredEntity createDataKey="getConfigAttributeOption2"/> <requiredEntity createDataKey="getConfigAttributeOption3"/> </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> + <!-- Add the third simple product to the configurable product --> <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> <requiredEntity createDataKey="createConfigProduct"/> @@ -105,17 +119,21 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> <actionGroup ref="logout" stepKey="logout"/> </after> + <!-- Open Product in Store Front Page --> <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront"/> <waitForPageLoad stepKey="waitForProductToLoad"/> + <!-- Verify category,Configurable product and initial price --> <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryInFrontPage"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductNameInStoreFront"/> <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeInitialPriceInStoreFront"/> <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createConfigProduct.sku$$" stepKey="seeProductSkuInStoreFront"/> <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStatusInStoreFront"/> + <!-- Verify First Child Product attribute option is displayed --> <see selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="seeOption1"/> + <!-- Select product Attribute option1, option2 and option3 and verify changes in the price --> <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="selectOption1"/> <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeChildProduct1PriceInStoreFront"/> @@ -123,6 +141,7 @@ <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeChildProduct2PriceInStoreFront"/> <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption3.label$$" stepKey="selectOption3"/> <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct3.price$$" stepKey="seeChildProduct3PriceInStoreFront"/> + <!-- Open Product Index Page and Filter First Child product --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> @@ -131,22 +150,27 @@ </actionGroup> <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="selectFirstRow"/> <waitForPageLoad stepKey="waitForProductPageToLoad"/> + <!-- Disable the product --> <click selector="{{AdminProductFormSection.enableProductLabel}}" stepKey="disableProduct"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> + <!-- Open Product Store Front Page --> <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront1"/> <waitForPageLoad stepKey="waitForProductToLoad1"/> + <!-- Verify category,configurable product and updated price --> <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryInFrontPage1"/> <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductNameInStoreFront1"/> <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeUpdatedProductPriceInStoreFront"/> <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createConfigProduct.sku$$" stepKey="seeProductSkuInStoreFront1"/> <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStatusInStoreFront1"/> + <!-- Verify product Attribute Option1 is not displayed --> <dontSee selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="dontSeeOption1"/> + <!--Select product Attribute option2 and option3 and verify changes in the price --> <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="selectTheOption2"/> <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeSecondChildProductPriceInStoreFront"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest.xml index f5650feeff71b..8d41b276334a6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest.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="AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest"> + <test name="AdminCheckConfigurableProductPriceWithOutOfStockChildProductTest" extends="AdminCheckConfigurableProductPriceWithDisabledChildProductTest"> <annotations> <stories value="Configurable Product"/> <title value="Check Price for Configurable Product when Child is Out of Stock"/> @@ -17,141 +17,9 @@ <testCaseId value="MC-13750"/> <group value="mtf_migrated"/> </annotations> - <before> - <!-- Login as Admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> - <!-- Create Default Category --> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <!-- Create an attribute with three 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> - <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeOption3"> - <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> - <!-- Get the third option of the attribute created --> - <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeOption3"> - <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"/> - <field key="price">10.00</field> - </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"/> - <field key="price">20.00</field> - </createData> - <!--Create a simple product and give it the attribute with the Third option --> - <createData entity="ApiSimpleTwo" stepKey="createConfigChildProduct3"> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption3"/> - <field key="price">30.00</field> - </createData> - <!-- Create the configurable product --> - <createData entity="ConfigurableProductThreeOptions" stepKey="createConfigProductOption"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigProductAttribute"/> - <requiredEntity createDataKey="getConfigAttributeOption1"/> - <requiredEntity createDataKey="getConfigAttributeOption2"/> - <requiredEntity createDataKey="getConfigAttributeOption3"/> - </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> - <!-- Add the third simple product to the configurable product --> - <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild3"> - <requiredEntity createDataKey="createConfigProduct"/> - <requiredEntity createDataKey="createConfigChildProduct3"/> - </createData> - </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="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <!-- Open Product Store Front Page --> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront"/> - <waitForPageLoad stepKey="waitForProductToLoad"/> - <!-- Verify category,Configurable product and initial price --> - <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryInFrontPage"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductNameInStoreFront"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeInitialProductPriceInStoreFront"/> - <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createConfigProduct.sku$$" stepKey="seeProductSkuInStoreFront"/> - <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStatusInStoreFront"/> - <!-- Verify First Child Product attribute option is displayed --> - <see selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="seeOption1"/> - <!-- Select product Attribute option1, option2 and option3 and verify changes in the price --> - <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="selectOption1"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct1.price$$" stepKey="seeChildProduct1PriceInStoreFront"/> - <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="selectOption2"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeChildProduct2PriceInStoreFront"/> - <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption3.label$$" stepKey="selectOption3"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct3.price$$" stepKey="seeChildProduct3PriceInStoreFront"/> - <!-- Open Product Index Page and Filter First Child product --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> - <actionGroup ref="filterProductGridBySku" stepKey="filterProduct"> - <argument name="product" value="ApiSimpleOne"/> - </actionGroup> - <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="selectFirstRow"/> - <waitForPageLoad stepKey="waitForProductPageToLoad"/> - <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrolllToProductQuantity"/> - <!-- Change the product stock status as 'Out Of Stock'--> - <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="disableProduct"/> - <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> - <!-- Open Product Store Front Page --> - <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront1"/> - <waitForPageLoad stepKey="waitForProductToLoad1"/> - <!-- Verify category,configurable product details and updated price --> - <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName($$createCategory.name$$)}}" stepKey="seeCategoryInFrontPage1"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductNameInStoreFront1"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeUpdatedProductPriceInStoreFront"/> - <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="$$createConfigProduct.sku$$" stepKey="seeProductSkuInStoreFront1"/> - <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStatusInStoreFront1"/> - <!-- Verify product Attribute Option1 is not displayed --> - <dontSee selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="dontSeeOption1"/> - <!-- Select product Attribute option2 and option3 and verify changes in the price --> - <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="selectTheOption2"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct2.price$$" stepKey="seeSecondChildProductPriceInStoreFront"/> - <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect($$createConfigProductAttribute.default_value$$)}}" userInput="$$getConfigAttributeOption3.label$$" stepKey="selectTheOption3"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createConfigChildProduct3.price$$" stepKey="seeThirdProductPriceInStoreFront"/> + + <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToProductQuantity" after="waitForProductPageToLoad"/> + <remove keyForRemoval="disableProduct"/> + <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="selectOutOfStock" after="scrollToProductQuantity"/> </test> </tests> From 6b0fc4791e49014386b2ea69f152cfe9cf93ec70 Mon Sep 17 00:00:00 2001 From: Joan He <johe@magento.com> Date: Tue, 19 Feb 2019 12:48:28 -0600 Subject: [PATCH 121/254] MC-14951: Create backup not working --- app/code/Magento/Backup/Controller/Adminhtml/Index.php | 3 +-- .../Magento/Backup/Controller/Adminhtml/Index/Create.php | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index.php b/app/code/Magento/Backup/Controller/Adminhtml/Index.php index 0edeb5565f288..b62963947d7bf 100644 --- a/app/code/Magento/Backup/Controller/Adminhtml/Index.php +++ b/app/code/Magento/Backup/Controller/Adminhtml/Index.php @@ -6,7 +6,6 @@ namespace Magento\Backup\Controller\Adminhtml; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Backup\Helper\Data as Helper; use Magento\Framework\App\ObjectManager; @@ -18,7 +17,7 @@ * @since 100.0.2 * @SuppressWarnings(PHPMD.AllPurposeAction) */ -abstract class Index extends Action implements HttpGetActionInterface +abstract class Index extends Action { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php b/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php index 53f45aff50cbc..99c48b727521a 100644 --- a/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php +++ b/app/code/Magento/Backup/Controller/Adminhtml/Index/Create.php @@ -1,15 +1,18 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Backup\Controller\Adminhtml\Index; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; -class Create extends \Magento\Backup\Controller\Adminhtml\Index +/** + * Create backup controller + */ +class Create extends \Magento\Backup\Controller\Adminhtml\Index implements HttpPostActionInterface { /** * Create backup action. From 29fa9ad7d5c1cdec6e64482c5e2c1f3c0e503dee Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 19 Feb 2019 14:15:24 -0600 Subject: [PATCH 122/254] MC-14957: Authorize.net trying to load non-existent Accept.min.js in production configurations - Changed how require loads accept.js library to work within all constraints --- .../view/base/requirejs-config.js | 14 ++++---------- .../base/web/js/view/payment/acceptjs-factory.js | 13 +++++++++++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js index 83ddd1094ea1a..cbe0a6c30e699 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js +++ b/app/code/Magento/AuthorizenetAcceptjs/view/base/requirejs-config.js @@ -4,16 +4,10 @@ */ var config = { - shim: { - acceptjs: { - exports: 'Accept' - }, - acceptjssandbox: { - exports: 'Accept' + map: { + '*': { + acceptjssandbox: 'https://jstest.authorize.net/v1/Accept.js', + acceptjs: 'https://js.authorize.net/v1/Accept.js' } - }, - 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-factory.js b/app/code/Magento/AuthorizenetAcceptjs/view/base/web/js/view/payment/acceptjs-factory.js index e98a204e36cee..c8813c17c70c7 100644 --- 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 @@ -16,7 +16,7 @@ define([ dependency = 'acceptjssandbox'; } - require([dependency], function (accept) { + require([dependency], function () { var $body = $('body'); /* @@ -26,7 +26,16 @@ define([ * Dynamically-loading-Accept-js-E-WC-03-Accept-js-is-not-loaded/td-p/63283 */ $body.on('handshake.acceptjs', function () { - deferred.resolve(accept); + /* + * Accept.js doesn't return the library when loading + * and requirejs "shim" can't be used because it only works with the "paths" config option + * and we can't use "paths" because require will try to load ".min.js" in production + * and that doesn't work because it doesn't exist + * and we can't add a query string to force a URL because accept.js will reject it + * and we can't include it locally because they check in the script before loading more scripts + * So, we use the global version as "shim" would + */ + deferred.resolve(window.Accept); $body.off('handshake.acceptjs'); }); }, From 55b8bf813f4e398a6c81e0725a8c16302612f745 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Wed, 13 Feb 2019 14:41:53 -0600 Subject: [PATCH 123/254] MC-14849: \Magento\Paypal\Model\Api\Nvp::callSetExpressCheckout returns empty response --- lib/internal/Magento/Framework/HTTP/Adapter/Curl.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php index 0e51c64661a4b..bc833bf3bb2d4 100644 --- a/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php +++ b/lib/internal/Magento/Framework/HTTP/Adapter/Curl.php @@ -170,7 +170,6 @@ 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_HTTP_VERSION, $http_ver); curl_setopt($this->_getResource(), CURLOPT_RETURNTRANSFER, true); if ($method == \Zend_Http_Client::POST) { curl_setopt($this->_getResource(), CURLOPT_POST, true); From f1877658834be6afecc8c47f9b567d6c91d0b651 Mon Sep 17 00:00:00 2001 From: John S <john00ivy@gmail.com> Date: Tue, 19 Feb 2019 15:05:22 -0600 Subject: [PATCH 124/254] MC-4409: Convert UpdateProductAttributeEntityTest to MFTF - Adding "waitForPageLoad" timeout value to the Action Group. - Replacing "waitForElementVisible" with "waitForPageLoad" action. --- ...ductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml | 4 ++-- ...ductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml | 4 ++-- .../Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 19553f6893fcb..1bc69be642a37 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -66,11 +66,11 @@ <!-- Change attribute property: Use in Search >Yes --> <scrollToTopOfPage stepKey="scrollToTabs"/> <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> - <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" stepKey="waitTabLoad"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="Yes" stepKey="seeInSearch"/> <!-- Change attribute property: Visible In Advanced Search >No --> - <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" stepKey="waitTabLoad2"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> <selectOption selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" userInput="No" stepKey="dontSeeInAdvancedSearch"/> <!-- Save the new product attributes --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index de373f246fa76..1f558568e9248 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -66,11 +66,11 @@ <!-- Change attribute property: Use in Search >Yes --> <scrollToTopOfPage stepKey="scrollToTabs"/> <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> - <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" stepKey="waitTabLoad"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> <selectOption selector="{{AdvancedAttributePropertiesSection.UseInSearch}}" userInput="Yes" stepKey="seeInSearch"/> <!-- Change attribute property: Visible In Advanced Search >No --> - <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" stepKey="waitTabLoad2"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> <selectOption selector="{{AdvancedAttributePropertiesSection.VisibleInAdvancedSearch}}" userInput="No" stepKey="dontSeeInAdvancedSearch"/> <!-- Save the new product attributes --> diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml index 387a7547f4daf..6b913e5b458e6 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontCatalogSearchActionGroup.xml @@ -48,7 +48,7 @@ <!-- Go to store's advanced catalog search page --> <actionGroup name="GoToStoreViewAdvancedCatalogSearchActionGroup"> <amOnPage url="{{StorefrontCatalogSearchAdvancedFormPage.url}}" stepKey="GoToStoreViewAdvancedCatalogSearchActionGroup"/> - <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForPageLoad time="90" stepKey="waitForPageLoad"/> </actionGroup> <!-- Storefront advanced catalog search by product name --> From eee25c633bd0eeac03f06f736fc27c8fd442fe87 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 19 Feb 2019 15:25:24 -0600 Subject: [PATCH 125/254] MC-4416: Convert CreateProductAttributeEntityTest to MFTF - Revert change to existing element name --- .../Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml | 2 +- .../Test/Mftf/Section/AdminProductAttributeGridSection.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml index 770efaf2fd9e0..46329dde278bc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductAttributeActionGroup.xml @@ -169,7 +169,7 @@ <arguments> <argument name="attribute" type="entity" defaultValue="productAttributeWysiwyg"/> </arguments> - <click stepKey="createNewAttribute" selector="{{AdminProductAttributeGridSection.NewAttribute}}"/> + <click stepKey="createNewAttribute" selector="{{AdminProductAttributeGridSection.createNewAttributeBtn}}"/> <fillField stepKey="fillDefaultLabel" selector="{{AttributePropertiesSection.DefaultLabel}}" userInput="{{attribute.attribute_code}}"/> <selectOption selector="{{AttributePropertiesSection.InputType}}" stepKey="checkInputType" userInput="{{attribute.frontend_input}}"/> <selectOption selector="{{AttributePropertiesSection.ValueRequired}}" stepKey="checkRequired" userInput="{{attribute.is_required_admin}}"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml index 41a6f005de4dd..12cc788ae06ae 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeGridSection"> <element name="AttributeCode" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> - <element name="NewAttribute" type="button" selector="#add"/> + <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> <element name="GridFilterFrontEndLabel" type="input" selector="#attributeGrid_filter_frontend_label"/> <element name="Search" type="button" selector="button[data-action=grid-filter-apply]" timeout="30"/> <element name="ResetFilter" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> From 8b09a6ff2e3b681cb92af8558ebbfdf6718bc576 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 19 Feb 2019 15:41:29 -0600 Subject: [PATCH 126/254] MAGETWO-98328: Update FedEx Shipping Dates behavior in Tracking Popup --- .../DataProviders/Tracking/ChangeTitle.php | 32 +++++++++++ .../Block/Tracking/PopupDeliveryDate.php | 55 +++++++++++++++++++ app/code/Magento/Fedex/etc/di.xml | 6 ++ .../Tracking/DeliveryDateTitle.php | 25 +++++++++ .../layout/shipping_tracking_popup.xml | 6 +- .../frontend/templates/tracking/details.phtml | 2 +- 6 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php create mode 100644 app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php create mode 100644 app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php diff --git a/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php new file mode 100644 index 0000000000000..d148b91aa3e7a --- /dev/null +++ b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Fedex\Plugin\Block\DataProviders\Tracking; + +use Magento\Fedex\Model\Carrier; +use Magento\Shipping\Model\Tracking\Result\Status; +use Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle as Subject; + +/** + * Plugin to change delivery date title with FedEx customized value + */ +class ChangeTitle +{ + /** + * @param Subject $subject + * @param \Magento\Framework\Phrase|string $result + * @param Status $trackingStatus + * @return \Magento\Framework\Phrase|string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterGetTitle(Subject $subject, $result, Status $trackingStatus): string + { + if ($trackingStatus->getCarrier() === Carrier::CODE) { + $result = __('Expected Delivery:'); + } + return $result; + } +} diff --git a/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php b/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php new file mode 100644 index 0000000000000..942d21dfa5e47 --- /dev/null +++ b/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Fedex\Plugin\Block\Tracking; + +use Magento\Shipping\Block\Tracking\Popup; +use Magento\Fedex\Model\Carrier; +use Magento\Shipping\Model\Tracking\Result\Status; + +/** + * Plugin to update delivery date value in case if Fedex used + */ +class PopupDeliveryDate +{ + /** + * Show only date for expected delivery in case if Fedex is a carrier + * + * @param Popup $subject + * @param string $result + * @param string $date + * @param string $time + * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterFormatDeliveryDateTime(Popup $subject, string $result, string $date, string $time): string + { + if ($this->getCarrier($subject) === Carrier::CODE) { + $result = $subject->formatDeliveryDate($date); + } + return $result; + } + + /** + * Retrieve carrier name from tracking info + * + * @param Popup $subject + * @return string + */ + private function getCarrier(Popup $subject): string + { + foreach ($subject->getTrackingInfo() as $trackingData) { + foreach ($trackingData as $trackingInfo) { + if ($trackingInfo instanceof Status) { + $carrier = $trackingInfo->getCarrier(); + return $carrier; + } + } + } + return ''; + } +} diff --git a/app/code/Magento/Fedex/etc/di.xml b/app/code/Magento/Fedex/etc/di.xml index f17f8f2afe663..c542b1f04d1eb 100644 --- a/app/code/Magento/Fedex/etc/di.xml +++ b/app/code/Magento/Fedex/etc/di.xml @@ -22,4 +22,10 @@ </argument> </arguments> </type> + <type name="Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle"> + <plugin name="update_delivery_date_title" type="Magento\Fedex\Plugin\Block\DataProviders\Tracking\ChangeTitle"/> + </type> + <type name="Magento\Shipping\Block\Tracking\Popup"> + <plugin name="update_delivery_date_value" type="Magento\Fedex\Plugin\Block\Tracking\PopupDeliveryDate"/> + </type> </config> diff --git a/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php new file mode 100644 index 0000000000000..dc66c4f0bd018 --- /dev/null +++ b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Shipping\Block\DataProviders\Tracking; + +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Shipping\Model\Tracking\Result\Status; + +/** + * Extension point to provide ability to change tracking details titles + */ +class DeliveryDateTitle implements ArgumentInterface +{ + /** + * @param Status $trackingStatus + * @return \Magento\Framework\Phrase|string + */ + public function getTitle(Status $trackingStatus) + { + return $trackingStatus->getCarrier() ? __('Delivered on:') : ''; + } +} diff --git a/app/code/Magento/Shipping/view/frontend/layout/shipping_tracking_popup.xml b/app/code/Magento/Shipping/view/frontend/layout/shipping_tracking_popup.xml index 1f5b0ae4630ad..67d03da2599bf 100644 --- a/app/code/Magento/Shipping/view/frontend/layout/shipping_tracking_popup.xml +++ b/app/code/Magento/Shipping/view/frontend/layout/shipping_tracking_popup.xml @@ -8,7 +8,11 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="empty" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> - <block class="Magento\Shipping\Block\Tracking\Popup" name="shipping.tracking.popup" template="Magento_Shipping::tracking/popup.phtml" cacheable="false" /> + <block class="Magento\Shipping\Block\Tracking\Popup" name="shipping.tracking.popup" template="Magento_Shipping::tracking/popup.phtml" cacheable="false"> + <arguments> + <argument name="delivery_date_title" xsi:type="object">Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle</argument> + </arguments> + </block> </referenceContainer> </body> </page> diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml index 9253b47f82f5d..e8584d8f6ad51 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml @@ -77,7 +77,7 @@ $number = is_object($track) ? $track->getTracking() : $track['number']; <?php if ($track->getDeliverydate()): ?> <tr> - <th class="col label" scope="row"><?= $block->escapeHtml(__('Delivered on:')) ?></th> + <th class="col label" scope="row"><?= $block->escapeHtml($parentBlock->getDeliveryDateTitle()->getTitle($track)) ?></th> <td class="col value"> <?= /* @noEscape */ $parentBlock->formatDeliveryDateTime($track->getDeliverydate(), $track->getDeliverytime()) ?> </td> From dab868344118dfb1ad355e3b6f3ea9355f445f0f Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 19 Feb 2019 15:49:16 -0600 Subject: [PATCH 127/254] MC-4416: Convert CreateProductAttributeEntityTest to MFTF - Add missing mtf_migrated group --- .../Test/Mftf/Test/CreateProductAttributeEntityTest.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml index 2290f5de0069d..52022f32fd8ec 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CreateProductAttributeEntityTest.xml @@ -17,6 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-10894"/> <group value="Catalog"/> + <group value="mtf_migrated"/> </annotations> <before> @@ -75,6 +76,7 @@ <skip> <issueId value="MC-13817"/> </skip> + <group value="mtf_migrated"/> </annotations> <before> @@ -134,6 +136,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-10897"/> <group value="Catalog"/> + <group value="mtf_migrated"/> </annotations> <before> @@ -187,6 +190,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-10896"/> <group value="Catalog"/> + <group value="mtf_migrated"/> </annotations> <before> @@ -273,6 +277,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-10898"/> <group value="Catalog"/> + <group value="mtf_migrated"/> </annotations> <before> @@ -341,6 +346,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-10888"/> <group value="Catalog"/> + <group value="mtf_migrated"/> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> From 763b52757deda085249070f69d59ac6be89c83b5 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Tue, 19 Feb 2019 16:23:36 -0600 Subject: [PATCH 128/254] MQE-1453: Deliver weekly PR - Add mtf_migrated tags to MTF tests --- ...SubcategoryNotIncludeInNavigationMenuTest.xml | 4 ++++ .../Product/UpdateSimpleProductEntityTest.xml | 16 ++++++++++++++-- .../CreateProductAttributeEntityTest.xml | 5 ++++- ...eteAssignedToTemplateProductAttributeTest.xml | 2 ++ .../UpdateProductAttributeEntityTest.xml | 2 ++ .../VerifyConfigurableProductEntityPriceTest.xml | 2 ++ 6 files changed, 28 insertions(+), 3 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/SubcategoryNotIncludeInNavigationMenuTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/SubcategoryNotIncludeInNavigationMenuTest.xml index 94d99dd6b7b24..53a7debffa438 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/SubcategoryNotIncludeInNavigationMenuTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/SubcategoryNotIncludeInNavigationMenuTest.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\Catalog\Test\TestCase\Category\MyTest" summary="Test child categories should not include in menu" ticketId="MAGETWO-72238"> <variation name="CategoryIncludeInNavigationMenuAndSubcategoryNotIncludeInNavigationMenu" summary="Active category and check that category is visible on navigation menu and subcategory is not visible on navigation menu"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="initialCategory/dataset" xsi:type="string">two_nested_categories</data> <data name="nestingLevel" xsi:type="number">2</data> <data name="category/data/is_active" xsi:type="string">Yes</data> @@ -16,6 +17,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertSubCategoryNotInNavigationMenu" /> </variation> <variation name="CategoryAndSubcategotyNotIncludeInNavigationMenu1" summary="Turn off include_in_menu category and check that category and subcategory are not visible on navigation menu"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="initialCategory/dataset" xsi:type="string">two_nested_categories</data> <data name="nestingLevel" xsi:type="number">2</data> <data name="category/data/is_active" xsi:type="string">Yes</data> @@ -24,6 +26,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertSubCategoryNotInNavigationMenu" /> </variation> <variation name="InactiveCategoryAndSubcategotyNotIncludeInNavigationMenu" summary="Inactive category and check that category and subcategory are not visible on navigation menu"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="initialCategory/dataset" xsi:type="string">two_nested_categories</data> <data name="nestingLevel" xsi:type="number">2</data> <data name="category/data/is_active" xsi:type="string">No</data> @@ -32,6 +35,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertSubCategoryNotInNavigationMenu" /> </variation> <variation name="CategoryAndSubcategotyNotIncludeInNavigationMenu2" summary="Turn off include_in_menu category, inactive category and check that category and subcategory are not visible on navigation menu"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="initialCategory/dataset" xsi:type="string">two_nested_categories</data> <data name="nestingLevel" xsi:type="number">2</data> <data name="category/data/is_active" xsi:type="string">No</data> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml index 2a46abdc2fd15..ce99a61c33bac 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.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\Catalog\Test\TestCase\Product\UpdateSimpleProductEntityTest" summary="Update Simple Product" ticketId="MAGETWO-23544"> <variation name="UpdateSimpleProductEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Update visibility to Catalog, Search</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> @@ -24,6 +25,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductPage" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Update visibility to Not Visible Individually</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> @@ -38,6 +40,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductIsNotDisplayingOnFrontend" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation3"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Update visibility to Catalog</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> @@ -55,6 +58,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductPage" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation4"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Update visibility to Search</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> @@ -72,6 +76,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductSearchableBySku" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation5"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Update stock to Out of Stock</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> @@ -89,6 +94,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductSearchableBySku" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation6"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Update product status to offline</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> @@ -103,6 +109,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductIsNotDisplayingOnFrontend" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation7"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Update category</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/category_ids/dataset" xsi:type="string">default</data> @@ -118,6 +125,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductVisibleInCategory" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation8" summary="Edit Simple Product" ticketId="MAGETWO-12428"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/category_ids/dataset" xsi:type="string">default</data> <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> @@ -128,13 +136,14 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductPage" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation9" summary="Unassign Products from the Category" ticketId="MAGETWO-12417"> - <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="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/category_ids/dataset" xsi:type="string"> -</data> <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductNotVisibleInCategory" /> </variation> <variation name="EditSimpleProductTestVariation10" summary="Edit product with enabled flat" ticketId="MAGETWO-21125"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="configData" xsi:type="string">product_flat</data> <data name="initialProduct/dataset" xsi:type="string">simple_10_dollar</data> <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> @@ -146,6 +155,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductInCategory" /> </variation> <variation name="EditSimpleProductTestVariation11" summary="Update simple product with custom option"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> <data name="product/data/sku" xsi:type="string">test_simple_product_%isolation%</data> @@ -160,6 +170,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductInCart" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation12" summary="Verify data overriding on Store View level" ticketId="MAGETWO-50640"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="store/dataset" xsi:type="string">custom</data> <data name="product/data/use_default_name" xsi:type="string">No</data> @@ -168,7 +179,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductNameOnDifferentStoreViews" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation13" summary="Price overriding on Store View level" ticketId="MAGETWO-58861"> - <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">price_scope_website</data> <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> <data name="store/dataset" xsi:type="string">custom</data> @@ -178,6 +189,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductPriceOnDifferentStoreViews" /> </variation> <variation name="UpdateSimpleProductEntityTestVariation14" summary="An error appears on open tier price with locale formatting" ticketId="MAGETWO-62076"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="initialProduct/dataset" xsi:type="string">simple_with_hight_tier_price</data> <constraint name="Magento\Catalog\Test\Constraint\AssertProductFormattingTierPrice" /> </variation> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/CreateProductAttributeEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/CreateProductAttributeEntityTest.xml index aae9ec6039f56..2287546aed102 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/CreateProductAttributeEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/CreateProductAttributeEntityTest.xml @@ -20,6 +20,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertAddedProductAttributeOnProductForm" /> </variation> <variation name="CreateProductAttributeEntityTestVariation2" summary="Create custom text attribute product field" ticketId="MAGETWO-17475"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="productAttribute/data/frontend_label" xsi:type="string">Text_Field_Admin_%isolation%</data> <data name="productAttribute/data/frontend_input" xsi:type="string">Text Area</data> @@ -115,7 +116,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertAttributeOptionsOnProductForm" /> </variation> <variation name="CreateProductAttributeEntityTestVariation6" summary="Create custom dropdown attribute product field" ticketId="MAGETWO-17475, MAGETWO-14862"> - <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="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="productAttribute/data/frontend_label" xsi:type="string">Dropdown_Admin_%isolation%</data> <data name="productAttribute/data/frontend_input" xsi:type="string">Dropdown</data> @@ -154,6 +155,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertAttributeOptionsOnProductForm" /> </variation> <variation name="CreateProductAttributeEntityTestVariation7" summary="Create custom price attribute product field" ticketId="MAGETWO-17475"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="productAttribute/data/frontend_label" xsi:type="string">Price_Admin_%isolation%</data> <data name="productAttribute/data/frontend_input" xsi:type="string">Price</data> @@ -209,6 +211,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeIsUnique" /> </variation> <variation name="CreateProductAttributeEntityTestVariation10" summary="Create custom dropdown attribute with single quotation in option"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="productAttribute/data/frontend_label" xsi:type="string">Dropdown_Admin_%isolation%</data> <data name="productAttribute/data/frontend_input" xsi:type="string">Dropdown</data> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/DeleteAssignedToTemplateProductAttributeTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/DeleteAssignedToTemplateProductAttributeTest.xml index b12f4f1ad7d94..d674535b54abb 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/DeleteAssignedToTemplateProductAttributeTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/DeleteAssignedToTemplateProductAttributeTest.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\Catalog\Test\TestCase\ProductAttribute\DeleteAssignedToTemplateProductAttributeTest" summary="Delete Assigned to Template Product Attribute" ticketId="MAGETWO-26011"> <variation name="DeleteAssignedToTemplateProductAttributeTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="attributeSet/data/assigned_attributes/dataset" xsi:type="string">attribute_type_dropdown</data> <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeSuccessDeleteMessage" /> @@ -16,6 +17,7 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductAttributeAbsenceInUnassignedAttributes" /> </variation> <variation name="DeleteAssignedToTemplateProductAttributeTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="attributeSet/dataset" xsi:type="string">default</data> <data name="attributeSet/data/assigned_attributes/dataset" xsi:type="string">attribute_type_text_field</data> <data name="assertProduct/data/name" xsi:type="string">Product name</data> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/UpdateProductAttributeEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/UpdateProductAttributeEntityTest.xml index b051d50b4acb6..40cf8e40ae33f 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/UpdateProductAttributeEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/ProductAttribute/UpdateProductAttributeEntityTest.xml @@ -67,6 +67,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchProductByAttribute" /> </variation> <variation name="UpdateProductAttributeEntityTestVariation4" summary="Create product attribute of type Dropdown and check its visibility on frontend in Advanced Search form" ticketId="MAGETWO-12941"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="productAttributeOriginal/dataset" xsi:type="string">attribute_type_dropdown</data> <data name="attribute/data/frontend_input" xsi:type="string">Dropdown</data> @@ -76,6 +77,7 @@ <constraint name="Magento\CatalogSearch\Test\Constraint\AssertAdvancedSearchAttributeIsAbsent" /> </variation> <variation name="UpdateProductAttributeEntityTestVariation5" summary="Create product attribute of type Multiple Select and check its visibility on frontend in Advanced Search form" ticketId="MAGETWO-12941"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="attributeSet/dataset" xsi:type="string">custom_attribute_set</data> <data name="productAttributeOriginal/dataset" xsi:type="string">attribute_type_multiple_select</data> <data name="attribute/data/frontend_label" xsi:type="string">Dropdown_%isolation%</data> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/VerifyConfigurableProductEntityPriceTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/VerifyConfigurableProductEntityPriceTest.xml index 6d22cea4689a8..d576e760179ed 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/VerifyConfigurableProductEntityPriceTest.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/VerifyConfigurableProductEntityPriceTest.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\ConfigurableProduct\Test\TestCase\VerifyConfigurableProductEntityPriceTest" summary="Verify price for configurable product"> <variation name="VerifyConfigurableProductEntityPriceTestVariation1" summary="Disable child product" ticketId="MAGETWO-60196"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product" xsi:type="string">configurableProduct::product_with_color</data> <data name="productUpdate/childProductUpdate" xsi:type="array"> <item name="data" xsi:type="array"> @@ -19,6 +20,7 @@ <constraint name="Magento\ConfigurableProduct\Test\Constraint\AssertConfigurableProductPage" /> </variation> <variation name="VerifyConfigurableProductEntityPriceTestVariation2" summary="Set child product Out of stock" ticketId="MAGETWO-60206"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product" xsi:type="string">configurableProduct::product_with_color</data> <data name="productUpdate/childProductUpdate" xsi:type="array"> <item name="data" xsi:type="array"> From 40890490c1a2430c3882ed9c721e4cd1e55a3879 Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Tue, 19 Feb 2019 10:42:11 -0600 Subject: [PATCH 129/254] MC-14953: Not clickable link --- .../Magento_Rma/web/css/source/_module.less | 14 +++++++++++--- .../Magento/Test/Legacy/_files/words_ce.xml | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/_module.less index c405707ee7bbe..16c84047b529d 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/_module.less @@ -7,13 +7,21 @@ .rma-request-details, .rma-wrapper .order-shipping-address { float: left; - #mix-grid .width(6,12); + /** + * @codingStandardsIgnoreStart + */ + #mix-grid .width(6, 12); + //@codingStandardsIgnoreEnd } .rma-confirmation, - .rma-wrapper .order-return-address { + .rma-wrapper .order-return-address, .rma-wrapper .order-shipping-method { float: right; - #mix-grid .width(6,12); + /** + * @codingStandardsIgnoreStart + */ + #mix-grid .width(6, 12); + //@codingStandardsIgnoreEnd } } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml index 9bb00533a5da5..92e7b15efed29 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/words_ce.xml @@ -69,5 +69,9 @@ <item> <path>dev/build/publication/sanity/ce.xml</path> </item> + <item> + <path>app/design/adminhtml/Magento/backend/Magento_Rma/web/css/source/_module.less</path> + <word>rma</word> + </item> </whitelist> </config> From 3c8ac9b608a98420f7ea271cbca7e84b2a70f509 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 19 Feb 2019 17:03:50 -0600 Subject: [PATCH 130/254] GraphQL-292: [Payment methods] Get list of available payment methods for current cart --- .../AvailablePaymentMethods.php} | 35 ++++++++++---- .../AvailablePaymentMethodsResolver.php | 48 ------------------- .../Magento/QuoteGraphQl/etc/schema.graphqls | 2 +- 3 files changed, 26 insertions(+), 59 deletions(-) rename app/code/Magento/QuoteGraphQl/Model/{Cart/PaymentMethod/AvailablePaymentMethodsDataProvider.php => Resolver/AvailablePaymentMethods.php} (52%) delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethodsResolver.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/PaymentMethod/AvailablePaymentMethodsDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethods.php similarity index 52% rename from app/code/Magento/QuoteGraphQl/Model/Cart/PaymentMethod/AvailablePaymentMethodsDataProvider.php rename to app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethods.php index daa51d8729995..907d778550593 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/PaymentMethod/AvailablePaymentMethodsDataProvider.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethods.php @@ -5,15 +5,19 @@ */ declare(strict_types=1); -namespace Magento\QuoteGraphQl\Model\Cart\PaymentMethod; +namespace Magento\QuoteGraphQl\Model\Resolver; use Magento\Checkout\Api\PaymentInformationManagementInterface; +use Magento\Framework\Exception\LocalizedException; +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\CartInterface; /** - * Get array of available payment methods. + * Get list of active payment methods resolver. */ -class AvailablePaymentMethodsDataProvider +class AvailablePaymentMethods implements ResolverInterface { /** * @var PaymentInformationManagementInterface @@ -21,7 +25,6 @@ class AvailablePaymentMethodsDataProvider private $informationManagement; /** - * AvailablePaymentMethodsDataProvider constructor. * @param PaymentInformationManagementInterface $informationManagement */ public function __construct(PaymentInformationManagementInterface $informationManagement) @@ -29,25 +32,37 @@ public function __construct(PaymentInformationManagementInterface $informationMa $this->informationManagement = $informationManagement; } + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + + $cart = $value['model']; + return $this->getPaymentMethodsData($cart); + } + /** * Collect and return information about available payment methods * * @param CartInterface $cart * @return array */ - public function getPaymentMethods(CartInterface $cart): array + private function getPaymentMethodsData(CartInterface $cart): array { $paymentInformation = $this->informationManagement->getPaymentInformation($cart->getId()); $paymentMethods = $paymentInformation->getPaymentMethods(); - $paymentMethodsNested = []; + $paymentMethodsData = []; foreach ($paymentMethods as $paymentMethod) { - $paymentMethodsNested[] = [ + $paymentMethodsData[] = [ 'title' => $paymentMethod->getTitle(), - 'code' => $paymentMethod->getCode() + 'code' => $paymentMethod->getCode(), ]; } - - return $paymentMethodsNested; + return $paymentMethodsData; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethodsResolver.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethodsResolver.php deleted file mode 100644 index 17747ec11b25a..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethodsResolver.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Resolver; - -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\QuoteGraphQl\Model\Cart\PaymentMethod\AvailablePaymentMethodsDataProvider; - -/** - * Get list of active payment methods resolver. - */ -class AvailablePaymentMethodsResolver implements ResolverInterface -{ - /** - * @var AvailablePaymentMethodsDataProvider - */ - private $addressDataProvider; - - /** - * @param AvailablePaymentMethodsDataProvider $addressDataProvider - */ - public function __construct( - AvailablePaymentMethodsDataProvider $addressDataProvider - ) { - $this->addressDataProvider = $addressDataProvider; - } - - /** - * @inheritdoc - */ - public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) - { - if (!isset($value['model'])) { - throw new LocalizedException(__('"model" value should be specified')); - } - - $cart = $value['model']; - - return $this->addressDataProvider->getPaymentMethods($cart); - } -} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index a505b87ccbae9..bab85f26b8b1e 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -104,7 +104,7 @@ type Cart { applied_coupon: AppliedCoupon shipping_addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddresses") billing_address: CartAddress! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\BillingAddress") - available_payment_methods : [CheckoutPaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethodsResolver") @doc(description: "Available payment methods") + available_payment_methods : [CheckoutPaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods") } type CartAddress { From fd114ecfbee9900e9abf8d6dfd8b14137c1ccad9 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 19 Feb 2019 17:06:20 -0600 Subject: [PATCH 131/254] GraphQL-292: [Payment methods] Get list of available payment methods for current cart --- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index bab85f26b8b1e..5749f830c1edc 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -104,7 +104,7 @@ type Cart { applied_coupon: AppliedCoupon shipping_addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddresses") billing_address: CartAddress! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\BillingAddress") - available_payment_methods : [CheckoutPaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods") + available_payment_methods : [PaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods") } type CartAddress { @@ -156,9 +156,9 @@ type AvailableShippingMethod { price_incl_tax: Float! } -type CheckoutPaymentMethod @doc(description: "The type contains list of active payment methods") { - code : String @doc(description: "The payment method code") - title : String @doc(description: "The payment method title.") +type PaymentMethod { + code: String @doc(description: "The payment method code") + title: String @doc(description: "The payment method title.") } enum AdressTypeEnum { From 974152f8ab4a7b7f381e9c28e37ac3c7adaf08c5 Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Wed, 20 Feb 2019 10:34:11 +0530 Subject: [PATCH 132/254] Applied some code format rules --- app/code/Magento/Marketplace/Block/Partners.php | 2 +- app/code/Magento/PageCache/Model/Cache/Server.php | 3 +-- .../PageCache/Model/System/Config/Backend/AccessList.php | 2 +- app/code/Magento/PageCache/Model/Varnish/VclGenerator.php | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Marketplace/Block/Partners.php b/app/code/Magento/Marketplace/Block/Partners.php index 4f8ca798f1756..a3ed88f4fc3b4 100644 --- a/app/code/Magento/Marketplace/Block/Partners.php +++ b/app/code/Magento/Marketplace/Block/Partners.php @@ -39,7 +39,7 @@ public function __construct( /** * Gets partners * - * @return bool|string + * @return array */ public function getPartners() { diff --git a/app/code/Magento/PageCache/Model/Cache/Server.php b/app/code/Magento/PageCache/Model/Cache/Server.php index 349e9faffa673..06118446c21bc 100644 --- a/app/code/Magento/PageCache/Model/Cache/Server.php +++ b/app/code/Magento/PageCache/Model/Cache/Server.php @@ -62,8 +62,7 @@ public function getUris() foreach ($configuredHosts as $host) { $servers[] = UriFactory::factory('') ->setHost($host['host']) - ->setPort(isset($host['port']) ? $host['port'] : self::DEFAULT_PORT) - ; + ->setPort(isset($host['port']) ? $host['port'] : self::DEFAULT_PORT); } } elseif ($this->request->getHttpHost()) { $servers[] = UriFactory::factory('')->setHost($this->request->getHttpHost())->setPort(self::DEFAULT_PORT); diff --git a/app/code/Magento/PageCache/Model/System/Config/Backend/AccessList.php b/app/code/Magento/PageCache/Model/System/Config/Backend/AccessList.php index e16584b0b17f8..7c9391ba22182 100644 --- a/app/code/Magento/PageCache/Model/System/Config/Backend/AccessList.php +++ b/app/code/Magento/PageCache/Model/System/Config/Backend/AccessList.php @@ -28,7 +28,7 @@ public function beforeSave() throw new LocalizedException( new Phrase( 'Access List value "%1" is not valid. ' - .'Please use only IP addresses and host names.', + . 'Please use only IP addresses and host names.', [$value] ) ); diff --git a/app/code/Magento/PageCache/Model/Varnish/VclGenerator.php b/app/code/Magento/PageCache/Model/Varnish/VclGenerator.php index cf5a703142c84..5a7c3a2d2f6d1 100644 --- a/app/code/Magento/PageCache/Model/Varnish/VclGenerator.php +++ b/app/code/Magento/PageCache/Model/Varnish/VclGenerator.php @@ -119,7 +119,7 @@ private function getReplacements() private function getRegexForDesignExceptions() { $result = ''; - $tpl = "%s (req.http.user-agent ~ \"%s\") {\n"." hash_data(\"%s\");\n"." }"; + $tpl = "%s (req.http.user-agent ~ \"%s\") {\n" . " hash_data(\"%s\");\n" . " }"; $expressions = $this->getDesignExceptions(); @@ -157,7 +157,7 @@ private function getTransformedAccessList() $result = array_reduce( $this->getAccessList(), function ($ips, $ip) use ($tpl) { - return $ips.sprintf($tpl, trim($ip)) . "\n"; + return $ips . sprintf($tpl, trim($ip)) . "\n"; }, '' ); From fbb5eacbf10e4b51c07c4f2e70f4c3a9d9cc10a1 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <l.lesechko@gmail.com> Date: Tue, 19 Feb 2019 23:34:14 -0600 Subject: [PATCH 133/254] Convert EditShippingAddressOnePageCheckoutTest to MFTF --- .../Catalog/Test/Mftf/Data/ProductData.xml | 17 ++++ .../FillShippingZipFormActionGroup.xml | 23 ++++++ .../StorefrontProductCartActionGroup.xml | 10 +++ .../Mftf/Section/CheckoutShippingSection.xml | 1 + ...ngAddressOnePageCheckoutTestVariation1.xml | 80 +++++++++++++++++++ ...EditShippingAddressOnePageCheckoutTest.xml | 1 + 6 files changed, 132 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTestVariation1.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index d136661e917cb..94c526de38605 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -729,4 +729,21 @@ <data key="attributeGroupId">13</data> <data key="sortOrder">0</data> </entity> + <entity name="simpleProductDefault" type="product"> + <data key="sku" unique="suffix">sku_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">Simple Product </data> + <data key="price">560</data> + <data key="urlKey" unique="suffix">simple-product-</data> + <data key="status">1</data> + <data key="quantity">25</data> + <data key="weight">1</data> + <data key="product_has_weight">1</data> + <data key="is_in_stock">1</data> + <data key="tax_class_id">2</data> + <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> + <requiredEntity type="custom_attribute_array">CustomAttributeCategoryIds</requiredEntity> + </entity> </entities> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml index f12bf4344ab12..1278ba3afb85b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml @@ -20,4 +20,27 @@ <fillField stepKey="fillPostCode" selector="{{CheckoutCartSummarySection.postcode}}" userInput="{{address.postcode}}"/> <waitForPageLoad stepKey="waitForFormUpdate"/> </actionGroup> + <actionGroup name="FillShippingAddressOneStreetActionGroup"> + <arguments> + <argument name="Address"/> + </arguments> + <fillField stepKey="fillFirstName" selector="{{CheckoutShippingSection.firstName}}" userInput="{{Address.firstname}}"/> + <fillField stepKey="fillLastName" selector="{{CheckoutShippingSection.lastName}}" userInput="{{Address.lastname}}"/> + <fillField stepKey="fillCompany" selector="{{CheckoutShippingSection.company}}" userInput="{{Address.company}}"/> + <fillField stepKey="fillPhoneNumber" selector="{{CheckoutShippingSection.telephone}}" userInput="{{Address.telephone}}"/> + <fillField stepKey="fillStreetAddress" selector="{{CheckoutShippingSection.street}}" userInput="{{Address.street[0]}}"/> + <fillField stepKey="fillCityName" selector="{{CheckoutShippingSection.city}}" userInput="{{Address.city}}"/> + <selectOption stepKey="selectCounty" selector="{{CheckoutShippingSection.country}}" userInput="{{Address.country_id}}"/> + <fillField stepKey="fillZip" selector="{{CheckoutShippingSection.postcode}}" userInput="{{Address.postcode}}"/> + </actionGroup> + <actionGroup name="clearShippingAddressActionGroup"> + <clearField selector="{{CheckoutShippingSection.firstName}}" stepKey="clearFieldFirstName"/> + <clearField selector="{{CheckoutShippingSection.company}}" stepKey="clearFieldCompany"/> + <clearField selector="{{CheckoutShippingSection.street}}" stepKey="clearFieldStreetAddress"/> + <clearField selector="{{CheckoutShippingSection.city}}" stepKey="clearFieldCityName"/> + <selectOption selector="{{CheckoutShippingSection.region}}" userInput="" stepKey="clearFieldRegion"/> + <clearField selector="{{CheckoutShippingSection.postcode}}" stepKey="clearFieldZip"/> + <selectOption selector="{{CheckoutShippingSection.country}}" userInput="" stepKey="clearFieldCounty"/> + <clearField selector="{{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index 24ed05583b6fb..fbba37c22c8af 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -112,4 +112,14 @@ <fillField stepKey="fillZip" selector="{{CheckoutCartSummarySection.postcode}}" userInput="{{taxCode.zip}}"/> <waitForPageLoad stepKey="waitForFormUpdate"/> </actionGroup> + <!-- Add Product to Cart from the category page and check message --> + <actionGroup name="StorefrontAddCategorySimpleProductToCartActionGroup"> + <arguments> + <argument name="product"/> + </arguments> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart" /> + <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage" /> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added {{product.name}} to your shopping cart." stepKey="assertSuccessMessage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index a182a3357a9ce..d825e10395145 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -27,6 +27,7 @@ <element name="country" type="select" selector="select[name=country_id]"/> <element name="telephone" type="input" selector="input[name=telephone]"/> <element name="saveAddress" type="button" selector=".action-save-address"/> + <element name="cancelChangeAddress" type="button" selector=".action-hide-popup"/> <element name="updateAddress" type="button" selector=".action-update"/> <element name="next" type="button" selector="button.button.action.continue.primary" timeout="30"/> <element name="firstShippingMethod" type="radio" selector="//*[@id='checkout-shipping-method-load']//input[@class='radio']"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTestVariation1.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTestVariation1.xml new file mode 100644 index 0000000000000..d1eb09af976c5 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTestVariation1.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="EditShippingAddressOnePageCheckoutTestVariation1"> + <annotations> + <features value="Checkout"/> + <stories value="Edit Shipping Address"/> + <title value="Edit Shipping Address on Checkout Page."/> + <description value="Edit Shipping Address on Checkout Page."/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-67837"/> + <group value="shoppingCart"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="simpleProductDefault" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer_NY" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + </after> + + <!-- Go to Frontend as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + + <!-- Add product to cart --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <actionGroup ref="StorefrontAddCategorySimpleProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + + <!-- Go to checkout page --> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="customerGoToCheckoutFromMinicart" /> + + <!-- *New Address* button on 1st checkout step --> + <click selector="{{CheckoutShippingSection.newAddressButton}}" stepKey="addNewAddress"/> + + <!--Fill in required fields and click *Save address* button--> + <actionGroup ref="FillShippingAddressOneStreetActionGroup" stepKey="changeAddress"> + <argument name="Address" value="UK_Not_Default_Address"/> + </actionGroup> + <click selector="{{CheckoutShippingSection.saveAddress}}" stepKey="saveNewAddress"/> + + <!--Select Shipping Rate--> + <scrollTo selector="{{CheckoutShippingMethodsSection.next}}" stepKey="scrollToShippingRate"/> + <click selector="{{CheckoutShippingMethodsSection.shippingMethodFlatRate}}" stepKey="selectShippingMethod"/> + + <!-- Click *Edit* button for the new address --> + <click selector="{{CheckoutShippingSection.editActiveAddress}}" stepKey="editNewAddress"/> + + <!--Remove values from required fields and click *Cancel* button --> + <actionGroup ref="clearShippingAddressActionGroup" stepKey="clearRequiredFields"/> + <click selector="{{CheckoutShippingSection.cancelChangeAddress}}" stepKey="cancelEditAddress"/> + + <!-- Go to *Next* --> + <scrollTo selector="{{CheckoutShippingMethodsSection.next}}" stepKey="scrollToButtonNext"/> + <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="goNext"/> + + <!-- Select payment solution --> + <checkOption selector="{{CheckoutPaymentSection.billingAddressNotSameCheckbox}}" stepKey="selectPaymentSolution" /> + + <!--Refresh Page and Place Order--> + <reloadPage stepKey="reloadPage"/> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="placeOrder"/> + </test> + </tests> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml index 3c88d9193db28..51bcad08ce349 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.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\Checkout\Test\TestCase\EditShippingAddressOnePageCheckoutTest" summary="Customer can place order with new addresses that was edited during checkout with several conditions" ticketId="MAGETWO-67837"> <variation name="EditShippingAddressOnePageCheckoutTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="tag" xsi:type="string">severity:S1</data> <data name="customer/dataset" xsi:type="string">johndoe_with_addresses</data> <data name="shippingAddress/dataset" xsi:type="string">UK_address_without_email</data> From f78a3e1b3c6130b8890f1952c38d430b3be04171 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 20 Feb 2019 08:27:57 +0200 Subject: [PATCH 134/254] MAGETWO-98087: [2.3] Customer Group Regression issues from MAGETWO-96886 --- app/code/Magento/Backend/Model/Session/Quote.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Backend/Model/Session/Quote.php b/app/code/Magento/Backend/Model/Session/Quote.php index e32f1bc57596e..ed0312874565c 100644 --- a/app/code/Magento/Backend/Model/Session/Quote.php +++ b/app/code/Magento/Backend/Model/Session/Quote.php @@ -24,6 +24,7 @@ * @method Quote setOrderId($orderId) * @method int getOrderId() * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Quote extends \Magento\Framework\Session\SessionManager From a29ecf4196e83723a39127202aa893b51cedb346 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 20 Feb 2019 10:31:30 +0200 Subject: [PATCH 135/254] graphQl-352: removed redundant dataprovider --- .../Customer/IsEmailAvailableDataProvider.php | 40 ------------------- .../Model/Resolver/IsEmailAvailable.php | 14 +++---- 2 files changed, 7 insertions(+), 47 deletions(-) delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Customer/IsEmailAvailableDataProvider.php diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/IsEmailAvailableDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Customer/IsEmailAvailableDataProvider.php deleted file mode 100644 index 9ec4ab9377ead..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/IsEmailAvailableDataProvider.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\CustomerGraphQl\Model\Customer; - -use Magento\Customer\Api\AccountManagementInterface; - -/** - * Is Customer Email Available checker - */ -class IsEmailAvailableDataProvider -{ - /** - * @var AccountManagementInterface - */ - private $accountManagement; - - /** - * @param AccountManagementInterface $accountManagement - */ - public function __construct(AccountManagementInterface $accountManagement) - { - $this->accountManagement = $accountManagement; - } - - /** - * Check is Email available - * - * @param string $email - * @return bool - */ - public function execute(string $email): bool - { - return $this->accountManagement->isEmailAvailable($email); - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php index a5edb78e67bfd..11ad0f77f8949 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php @@ -7,7 +7,7 @@ namespace Magento\CustomerGraphQl\Model\Resolver; -use Magento\CustomerGraphQl\Model\Customer\IsEmailAvailableDataProvider; +use Magento\Customer\Api\AccountManagementInterface; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -19,17 +19,17 @@ class IsEmailAvailable implements ResolverInterface { /** - * @var IsEmailAvailableDataProvider + * @var AccountManagementInterface */ - private $isEmailAvailableDataProvider; + private $accountManagement; /** - * @param IsEmailAvailableDataProvider $isEmailAvailableDataProvider + * @param AccountManagementInterface $accountManagement */ public function __construct( - IsEmailAvailableDataProvider $isEmailAvailableDataProvider + AccountManagementInterface $accountManagement ) { - $this->isEmailAvailableDataProvider = $isEmailAvailableDataProvider; + $this->accountManagement = $accountManagement; } /** @@ -46,7 +46,7 @@ public function resolve( if (!$email) { throw new GraphQlInputException(__('"Email should be specified')); } - $isEmailAvailable = $this->isEmailAvailableDataProvider->execute($email); + $isEmailAvailable = $this->accountManagement->isEmailAvailable($email); return [ 'is_email_available' => $isEmailAvailable From 7c42d2afd9ca4d182d473b95dd2b4afe1c0152cc Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Wed, 20 Feb 2019 11:09:27 +0200 Subject: [PATCH 136/254] graphQl-292: added test coverage for payment methods --- .../Quote/GetAvailablePaymentMethodsTest.php | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailablePaymentMethodsTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailablePaymentMethodsTest.php new file mode 100644 index 0000000000000..d6eff6816b342 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailablePaymentMethodsTest.php @@ -0,0 +1,152 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for getting cart information + */ +class GetAvailablePaymentMethodsTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var Quote + */ + private $quote; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->create(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + */ + public function testGetCartWithPaymentMethodsForRegisteredCustomer() + { + $reservedOrderId = 'test_order_item_with_items'; + $this->quoteResource->load( + $this->quote, + $reservedOrderId, + 'reserved_order_id' + ); + + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $query = $this->prepareGetCartQuery($maskedQuoteId); + + $response = $this->sendRequestWithToken($query); + + self::assertArrayHasKey('cart', $response); + self::assertNotEmpty($response['cart']['items']); + self::assertNotEmpty($response['cart']['available_payment_methods']); + self::assertCount(1, $response['cart']['available_payment_methods']); + self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); + self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + */ + public function testGetCartWithPaymentMethodsForGuest() + { + $reservedOrderId = 'test_order_1'; + $this->quoteResource->load( + $this->quote, + $reservedOrderId, + 'reserved_order_id' + ); + + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $query = $this->prepareGetCartQuery($maskedQuoteId); + + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response); + + self::assertNotEmpty($response['cart']['available_payment_methods']); + self::assertCount(2, $response['cart']['available_payment_methods']); + self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); + self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); + self::assertEquals('free', $response['cart']['available_payment_methods'][1]['code']); + self::assertEquals( + 'No Payment Information Required', + $response['cart']['available_payment_methods'][1]['title'] + ); + } + + /** + * Generates query for setting the specified shipping method on cart + * + * @param string $maskedQuoteId + * @return string + */ + private function prepareGetCartQuery( + string $maskedQuoteId + ) : string { + return <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + applied_coupon { + code + } + items { + id + } + available_payment_methods { + code, + title + } + } +} + +QUERY; + } + + /** + * Sends a GraphQL request with using a bearer token + * + * @param string $query + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function sendRequestWithToken(string $query): array + { + + $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + + return $this->graphQlQuery($query, [], '', $headerMap); + } +} From 0df8d31c7b55c9fe49339df24ae45170749054f8 Mon Sep 17 00:00:00 2001 From: mageprince <mail.mageprince@gmail.com> Date: Wed, 20 Feb 2019 16:56:11 +0530 Subject: [PATCH 137/254] Show error message if no any product slected from recent order group --- app/code/Magento/Checkout/Controller/Cart/Addgroup.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php index c205f3c16072f..948d55c4c98a4 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php +++ b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php @@ -74,6 +74,8 @@ public function execute() } } $this->cart->save(); + } else { + $this->messageManager->addErrorMessage(__('Please select atleast one product to add to cart')); } return $this->_goBack(); } From 78916b3f987d47febbdf443b8839baca780e9c02 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Wed, 20 Feb 2019 13:30:27 +0200 Subject: [PATCH 138/254] Fix static tests. --- lib/internal/Magento/Framework/Module/ModuleList/Loader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php index 80958f65e110e..72421f793f131 100644 --- a/lib/internal/Magento/Framework/Module/ModuleList/Loader.php +++ b/lib/internal/Magento/Framework/Module/ModuleList/Loader.php @@ -134,7 +134,7 @@ private function sortBySequence(array $origList): array $modules = $this->prearrangeModules($origList); $expanded = []; - foreach ($modules as $moduleName => $value) { + foreach (array_keys($modules) as $moduleName) { $sequence = $this->expandSequence($origList, $moduleName); asort($sequence); From b63123e693fe7f950318ed6550d71494661fea89 Mon Sep 17 00:00:00 2001 From: mageprince <mail.mageprince@gmail.com> Date: Wed, 20 Feb 2019 17:12:37 +0530 Subject: [PATCH 139/254] Solve page break on long string search --- .../Magento/luma/Magento_Theme/web/css/source/_module.less | 1 + 1 file changed, 1 insertion(+) 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 99da7716f9274..9f0997ed2ba85 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 @@ -503,6 +503,7 @@ > .page-title-wrapper { .page-title { display: inline-block; + word-break: break-all; } .page-title + .action { From 1236770b10b14969a69ac81725a6bab781011ffd Mon Sep 17 00:00:00 2001 From: mageprince <mail.mageprince@gmail.com> Date: Wed, 20 Feb 2019 17:30:37 +0530 Subject: [PATCH 140/254] Move css to general for all devices --- .../luma/Magento_Theme/web/css/source/_module.less | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 9f0997ed2ba85..4d990a82cb7e4 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 @@ -77,6 +77,14 @@ .lib-vendor-prefix-flex-grow(1); } + .page-main { + > .page-title-wrapper { + .page-title { + word-break: break-all; + } + } + } + // // Header // --------------------------------------------- @@ -503,7 +511,6 @@ > .page-title-wrapper { .page-title { display: inline-block; - word-break: break-all; } .page-title + .action { From adccc57a67718d5e527e08308e467c78d02d2568 Mon Sep 17 00:00:00 2001 From: mageprince <mail.mageprince@gmail.com> Date: Wed, 20 Feb 2019 17:44:40 +0530 Subject: [PATCH 141/254] Change error message --- app/code/Magento/Checkout/Controller/Cart/Addgroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php index 948d55c4c98a4..f32fd9504c77d 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php +++ b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php @@ -75,7 +75,7 @@ public function execute() } $this->cart->save(); } else { - $this->messageManager->addErrorMessage(__('Please select atleast one product to add to cart')); + $this->messageManager->addErrorMessage(__('Please select at least one product to add to cart')); } return $this->_goBack(); } From b8bbd973153808c97205b239e5ec3c1b815d9960 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Wed, 20 Feb 2019 15:21:38 +0200 Subject: [PATCH 142/254] MAGETWO-97396: Tax applied on child product is based on Tax Class of parent product --- .../Sales/Total/Quote/CommonTaxCollector.php | 44 +++++++++ .../Total/Quote/CommonTaxCollectorTest.php | 94 +++++++++++++++++++ .../Magento/ConfigurableProduct/composer.json | 3 +- .../Magento/ConfigurableProduct/etc/di.xml | 3 + 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/ConfigurableProduct/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php create mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php diff --git a/app/code/Magento/ConfigurableProduct/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/ConfigurableProduct/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php new file mode 100644 index 0000000000000..8bdde2aeb0cff --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Plugin\Tax\Model\Sales\Total\Quote; + +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Quote\Model\Quote\Item\AbstractItem; +use Magento\Tax\Api\Data\QuoteDetailsItemInterface; +use Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory; + +/** + * Plugin for CommonTaxCollector to apply Tax Class ID from child item for configurable product + */ +class CommonTaxCollector +{ + /** + * Apply Tax Class ID from child item for configurable product + * + * @param \Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector $subject + * @param QuoteDetailsItemInterface $result + * @param QuoteDetailsItemInterfaceFactory $itemDataObjectFactory + * @param AbstractItem $item + * @return QuoteDetailsItemInterface + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterMapItem( + \Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector $subject, + QuoteDetailsItemInterface $result, + QuoteDetailsItemInterfaceFactory $itemDataObjectFactory, + AbstractItem $item + ) : QuoteDetailsItemInterface { + if ($item->getProduct()->getTypeId() === Configurable::TYPE_CODE && $item->getHasChildren()) { + $childItem = $item->getChildren()[0]; + $result->getTaxClassKey()->setValue($childItem->getProduct()->getTaxClassId()); + } + + return $result; + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php new file mode 100644 index 0000000000000..9007eed40ae9b --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Test\Unit\Plugin\Tax\Model\Sales\Total\Quote; + +use Magento\Catalog\Model\Product; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\ConfigurableProduct\Plugin\Tax\Model\Sales\Total\Quote\CommonTaxCollector as CommonTaxCollectorPlugin; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\Quote\Item\AbstractItem; +use Magento\Tax\Api\Data\QuoteDetailsItemInterface; +use Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory; +use Magento\Tax\Api\Data\TaxClassKeyInterface; +use Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Test for CommonTaxCollector plugin + */ +class CommonTaxCollectorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CommonTaxCollectorPlugin + */ + private $commonTaxCollectorPlugin; + + /** + * @inheritdoc + */ + public function setUp() + { + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->commonTaxCollectorPlugin = $this->objectManager->getObject(CommonTaxCollectorPlugin::class); + } + + /** + * Test to apply Tax Class Id from child item for configurable product + */ + public function testAfterMapItem() + { + $childTaxClassId = 10; + + /** @var Product|MockObject $childProductMock */ + $childProductMock = $this->createPartialMock( + Product::class, + ['getTaxClassId'] + ); + $childProductMock->method('getTaxClassId')->willReturn($childTaxClassId); + /* @var AbstractItem|MockObject $quoteItemMock */ + $childQuoteItemMock = $this->createMock( + AbstractItem::class + ); + $childQuoteItemMock->method('getProduct')->willReturn($childProductMock); + + /** @var Product|MockObject $productMock */ + $productMock = $this->createPartialMock( + Product::class, + ['getTypeId'] + ); + $productMock->method('getTypeId')->willReturn(Configurable::TYPE_CODE); + /* @var AbstractItem|MockObject $quoteItemMock */ + $quoteItemMock = $this->createPartialMock( + AbstractItem::class, + ['getProduct', 'getHasChildren', 'getChildren', 'getQuote', 'getAddress', 'getOptionByCode'] + ); + $quoteItemMock->method('getProduct')->willReturn($productMock); + $quoteItemMock->method('getHasChildren')->willReturn(true); + $quoteItemMock->method('getChildren')->willReturn([$childQuoteItemMock]); + + /* @var TaxClassKeyInterface|MockObject $taxClassObjectMock */ + $taxClassObjectMock = $this->createMock(TaxClassKeyInterface::class); + $taxClassObjectMock->method('setValue')->with($childTaxClassId); + + /* @var QuoteDetailsItemInterface|MockObject $quoteDetailsItemMock */ + $quoteDetailsItemMock = $this->createMock(QuoteDetailsItemInterface::class); + $quoteDetailsItemMock->method('getTaxClassKey')->willReturn($taxClassObjectMock); + + $this->commonTaxCollectorPlugin->afterMapItem( + $this->createMock(CommonTaxCollector::class), + $quoteDetailsItemMock, + $this->createMock(QuoteDetailsItemInterfaceFactory::class), + $quoteItemMock + ); + } +} diff --git a/app/code/Magento/ConfigurableProduct/composer.json b/app/code/Magento/ConfigurableProduct/composer.json index e795ea7cd3618..76096fd6bdf67 100644 --- a/app/code/Magento/ConfigurableProduct/composer.json +++ b/app/code/Magento/ConfigurableProduct/composer.json @@ -25,7 +25,8 @@ "magento/module-sales-rule": "*", "magento/module-product-video": "*", "magento/module-configurable-sample-data": "*", - "magento/module-product-links-sample-data": "*" + "magento/module-product-links-sample-data": "*", + "magento/module-tax": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml index 0ae9ffde66f43..06134fcbf09d8 100644 --- a/app/code/Magento/ConfigurableProduct/etc/di.xml +++ b/app/code/Magento/ConfigurableProduct/etc/di.xml @@ -245,4 +245,7 @@ <type name="Magento\SalesRule\Model\Rule\Condition\Product"> <plugin name="apply_rule_on_configurable_children" type="Magento\ConfigurableProduct\Plugin\SalesRule\Model\Rule\Condition\Product" /> </type> + <type name="Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector"> + <plugin name="apply_tax_class_id" type="Magento\ConfigurableProduct\Plugin\Tax\Model\Sales\Total\Quote\CommonTaxCollector" /> + </type> </config> From f5e4fb76e8473e50b388cc5a0588d8f2852cdad6 Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Wed, 20 Feb 2019 08:28:24 -0600 Subject: [PATCH 143/254] MC-4543: Convert MassAssignCustomerGroupTest to MFTF - Adding Tests for Mass Assigning Customers to Groups --- .../AdminDeleteCustomerGroupActionGroup.xml | 4 +- .../AdminFilterCustomerByNameActionGroup.xml | 20 ++++++ .../AdminSelectAllCustomersActionGroup.xml | 14 ++++ .../AdminSelectCustomerByEmailActionGroup.xml | 17 +++++ .../NavigateCustomerActionGroup.xml | 15 ++++ .../NavigateCustomerGroupActionGroup.xml | 15 ++++ .../SetGroupCustomerActionGroup.xml | 22 ++++++ .../VerifyGroupCustomerActionGroup.xml | 20 ++++++ .../Test/Mftf/Data/CustomerGroupData.xml | 5 ++ .../Mftf/Section/AdminCustomerGridSection.xml | 3 + .../AdminEditCustomerInformationSection.xml | 1 + .../Mftf/Section/CustomersPageSection.xml | 2 + .../Mftf/Test/ChangeCustomerGroupTest.xml | 70 +++++++++++++++++++ 13 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminFilterCustomerByNameActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSelectAllCustomersActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSelectCustomerByEmailActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/SetGroupCustomerActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyGroupCustomerActionGroup.xml create mode 100644 app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml index 2609f0ab5c0d6..788e5f8967f43 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerGroupActionGroup.xml @@ -18,8 +18,8 @@ <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="cleanFiltersIfTheySet"/> <fillField userInput="{{customerGroupName}}" selector="{{AdminDataGridHeaderSection.filterFieldInput('customer_group_code')}}" stepKey="fillNameFieldOnFiltersSection"/> <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminCustomerGroupGridActionsSection.selectButton('customerGroupName')}}" stepKey="clickSelectButton"/> - <click selector="{{AdminCustomerGroupGridActionsSection.deleteAction('customerGroupName')}}" stepKey="clickOnDeleteItem"/> + <click selector="{{AdminCustomerGroupGridActionsSection.selectButton(customerGroupName)}}" stepKey="clickSelectButton"/> + <click selector="{{AdminCustomerGroupGridActionsSection.deleteAction(customerGroupName)}}" stepKey="clickOnDeleteItem"/> <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDeleteCustomerGroup"/> <seeElement selector="{{AdminMessagesSection.success}}" stepKey="seeSuccessMessage"/> </actionGroup> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminFilterCustomerByNameActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminFilterCustomerByNameActionGroup.xml new file mode 100644 index 0000000000000..c49a0dbe20ae7 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminFilterCustomerByNameActionGroup.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="AdminFilterCustomerByName"> + <arguments> + <argument name="customerName" type="string"/> + </arguments> + <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersSectionOnCustomerGroupIndexPage"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="cleanFiltersIfTheySet"/> + <fillField userInput="{{customerName}}" selector="{{AdminDataGridHeaderSection.filterFieldInput('name')}}" stepKey="fillNameFieldOnFiltersSection"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSelectAllCustomersActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSelectAllCustomersActionGroup.xml new file mode 100644 index 0000000000000..1a8b4da67e74a --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSelectAllCustomersActionGroup.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="AdminSelectAllCustomers"> + <checkOption selector="{{AdminCustomerGridMainActionsSection.multicheck}}" stepKey="checkAllCustomers"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSelectCustomerByEmailActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSelectCustomerByEmailActionGroup.xml new file mode 100644 index 0000000000000..bb84d578fd9ed --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSelectCustomerByEmailActionGroup.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="AdminSelectCustomerByEmail"> + <arguments> + <argument name="customerEmail" type="string"/> + </arguments> + <checkOption selector="{{AdminCustomerGridSection.customerCheckboxByEmail(customerEmail)}}" stepKey="checkCustomerBox"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerActionGroup.xml new file mode 100644 index 0000000000000..be639d245f022 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerActionGroup.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="NavigateToAllCustomerPage"> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.xml new file mode 100644 index 0000000000000..076797f349107 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/NavigateCustomerGroupActionGroup.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="NavigateToCustomerGroupPage"> + <amOnPage url="{{AdminCustomerGroupPage.url}}" stepKey="openCustomersGridPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SetGroupCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SetGroupCustomerActionGroup.xml new file mode 100644 index 0000000000000..d1123ea858f0b --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SetGroupCustomerActionGroup.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="SetCustomerGroupForSelectedCustomer"> + <arguments> + <argument name="groupName" type="string"/> + </arguments> + <click selector="{{CustomersPageSection.actions}}" stepKey="clickActions"/> + <click selector="{{CustomersPageSection.actionItem('Assign a Customer Group')}}" stepKey="clickAssignAction"/> + <executeJS function="document.getElementsByClassName('action-menu _active')[0].scrollBy(0, 10000)" stepKey="scrollToGroup"/> + <click selector="{{CustomersPageSection.assignGroup(groupName)}}" stepKey="selectGroup"/> + <waitForPageLoad stepKey="waitAfterSelectingGroup"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="acceptModal"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyGroupCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyGroupCustomerActionGroup.xml new file mode 100644 index 0000000000000..712d3a59a2144 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/VerifyGroupCustomerActionGroup.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="VerifyCustomerGroupForCustomer"> + <arguments> + <argument name="customerEmail" type="string"/> + <argument name="groupName" type="string"/> + </arguments> + <click selector="{{AdminCustomerGridSection.customerEditLinkByEmail(customerEmail)}}" stepKey="openCustomerPage"/> + <waitForPageLoad stepKey="waitForCustomerPage"/> + <see userInput="{{groupName}}" selector="{{AdminEditCustomerInformationSection.group}}" stepKey="checkCustomerGroup"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml index c1f11c9e9c390..6b4f3fc9d6b6e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerGroupData.xml @@ -13,6 +13,11 @@ <data key="tax_class_id">3</data> <data key="tax_class_name">Retail Customer</data> </entity> + <entity name="CustomerGroupChange" type="customerGroup"> + <data key="code" unique="suffix">Group_</data> + <data key="tax_class_id">3</data> + <data key="tax_class_name">Retail Customer</data> + </entity> <entity name="DefaultCustomerGroup" type="customerGroup"> <array key="group_names"> <item>General</item> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml index d9d3bfe7f737c..b3c4e2361fdcb 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml @@ -11,5 +11,8 @@ <section name="AdminCustomerGridSection"> <element name="customerGrid" type="text" selector="table[data-role='grid']"/> <element name="firstRowEditLink" type="text" selector="tr[data-repeat-index='0'] .action-menu-item" timeout="30"/> + <element name="customerCheckboxByEmail" type="checkbox" selector="//tr[@class='data-row' and //div[text()='{{customerEmail}}']]//input[@type='checkbox']" parameterized="true" timeout="30"/> + <element name="customerEditLinkByEmail" type="text" selector="//tr[@class='data-row' and //div[text()='{{customerEmail}}']]//a[@class='action-menu-item']" parameterized="true" timeout="30"/> + <element name="customerGroupByEmail" type="text" selector="//tr[@class='data-row' and //div[text()='{{customerEmail}}']]//div[text()='{{customerGroup}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml index f5bbb84eaa593..8c2e481bc908b 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerInformationSection.xml @@ -11,5 +11,6 @@ <section name="AdminEditCustomerInformationSection"> <element name="orders" type="button" selector="#tab_orders_content" timeout="30"/> <element name="addresses" type="button" selector="//a[@id='tab_address']" timeout="30"/> + <element name="group" type="text" selector="//th[text()='Customer Group:']/../td"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml index 60c635387199a..1af43c9aa81f0 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml @@ -12,6 +12,8 @@ <element name="addNewCustomerButton" type="button" selector="//*[@id='add']"/> <element name="customerCheckbox" type="button" selector="//*[contains(text(),'{{args}}')]/parent::td/preceding-sibling::td/label[@class='data-grid-checkbox-cell-inner']" parameterized="true"/> <element name="actions" type="button" selector="//div[@class='col-xs-2']/div[@class='action-select-wrap']/button[@class='action-select']"/> + <element name="actionItem" type="button" selector="//div[@class='admin__data-grid-outer-wrap']/div[@class='admin__data-grid-header']//span[text()='{{actionItem}}']" parameterized="true" timeout="30"/> + <element name="assignGroup" type="button" selector="//div[@class='admin__data-grid-outer-wrap']/div[@class='admin__data-grid-header']//ul[@class='action-submenu _active']//span[text()='{{groupName}}']" parameterized="true"/> <element name="delete" type="button" selector="//*[contains(@class,'admin__data-grid-header-row row row-gutter')]//*[text()='Delete']"/> <element name="ok" type="button" selector="//button[@data-role='action']//span[text()='OK']"/> <element name="deletedSuccessMessage" type="button" selector="//*[@class='message message-success success']"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml new file mode 100644 index 0000000000000..90ea5c2be8c97 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.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="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"/> + </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> + </after> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <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="$createCustomer.fullname$"/> + </actionGroup> + <actionGroup ref="AdminSelectCustomerByEmail" stepKey="selectCustomer"> + <argument name="customerEmail" value="$createCustomer.email$"/> + </actionGroup> + <actionGroup ref="SetCustomerGroupForSelectedCustomer" stepKey="setCustomerGroup"> + <argument name="groupName" value="{{CustomerGroupChange.code}}"/> + </actionGroup> + <actionGroup ref="AdminFilterCustomerByName" stepKey="filterCustomerAfterGroupChange"> + <argument name="customerName" value="$createCustomer.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"/> + </annotations> + <remove keyForRemoval="filterCustomer"/> + <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"> + <argument name="customerEmail" value="$customer.email$"/> + </actionGroup> + </test> +</tests> From 544485428178dd0a107f7fdf9df4a753a6a455d9 Mon Sep 17 00:00:00 2001 From: serhii balko <serhii.balko@transoftgroup.com> Date: Wed, 20 Feb 2019 17:00:01 +0200 Subject: [PATCH 144/254] MAGETWO-97396: Tax applied on child product is based on Tax Class of parent product --- .../Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php index 9007eed40ae9b..1a5c6c0003bfa 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/Tax/Model/Sales/Total/Quote/CommonTaxCollectorTest.php @@ -78,7 +78,7 @@ public function testAfterMapItem() /* @var TaxClassKeyInterface|MockObject $taxClassObjectMock */ $taxClassObjectMock = $this->createMock(TaxClassKeyInterface::class); - $taxClassObjectMock->method('setValue')->with($childTaxClassId); + $taxClassObjectMock->expects($this->once())->method('setValue')->with($childTaxClassId); /* @var QuoteDetailsItemInterface|MockObject $quoteDetailsItemMock */ $quoteDetailsItemMock = $this->createMock(QuoteDetailsItemInterface::class); From ada2a0464fd0f719d3ce2dba483877517e5cb684 Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Wed, 20 Feb 2019 09:29:22 -0600 Subject: [PATCH 145/254] MC-4543: Convert MassAssignCustomerGroupTest to MFTF - Making action group clearer --- .../Test/Mftf/ActionGroup/SetGroupCustomerActionGroup.xml | 2 +- .../Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SetGroupCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SetGroupCustomerActionGroup.xml index d1123ea858f0b..ca5e16c4ddb40 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SetGroupCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SetGroupCustomerActionGroup.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="SetCustomerGroupForSelectedCustomer"> + <actionGroup name="SetCustomerGroupForSelectedCustomersViaGrid"> <arguments> <argument name="groupName" type="string"/> </arguments> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml index 90ea5c2be8c97..9948ba3cf6cfe 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml @@ -41,7 +41,7 @@ <actionGroup ref="AdminSelectCustomerByEmail" stepKey="selectCustomer"> <argument name="customerEmail" value="$createCustomer.email$"/> </actionGroup> - <actionGroup ref="SetCustomerGroupForSelectedCustomer" stepKey="setCustomerGroup"> + <actionGroup ref="SetCustomerGroupForSelectedCustomersViaGrid" stepKey="setCustomerGroup"> <argument name="groupName" value="{{CustomerGroupChange.code}}"/> </actionGroup> <actionGroup ref="AdminFilterCustomerByName" stepKey="filterCustomerAfterGroupChange"> From e0d5a9d5df287b7394fd2788e64f3f70dbfcf945 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 20 Feb 2019 09:37:20 -0600 Subject: [PATCH 146/254] MQE-1453: Deliver weekly PR - Fix reference to incorrect Add New Product Attribute button --- .../Test/Mftf/Section/AdminProductAttributeGridSection.xml | 2 +- .../AdminCreateCustomProductAttributeWithDropdownFieldTest.xml | 2 +- .../Test/AdminCreateProductAttributeFromProductPageTest.xml | 2 +- .../Test/AdminCreateProductAttributeRequiredTextFieldTest.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml index 12cc788ae06ae..5efd04eacb719 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductAttributeGridSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminProductAttributeGridSection"> <element name="AttributeCode" type="text" selector="//td[contains(text(),'{{var1}}')]" parameterized="true" timeout="30"/> - <element name="createNewAttributeBtn" type="button" selector="button[data-index='add_new_attribute_button']"/> + <element name="createNewAttributeBtn" type="button" selector="#add"/> <element name="GridFilterFrontEndLabel" type="input" selector="#attributeGrid_filter_frontend_label"/> <element name="Search" type="button" selector="button[data-action=grid-filter-apply]" timeout="30"/> <element name="ResetFilter" type="button" selector="button[data-action='grid-filter-reset']" timeout="30"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml index 5b6a0b7f2ab3e..09b49011938e8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCustomProductAttributeWithDropdownFieldTest.xml @@ -59,7 +59,7 @@ <!-- Create New Product Attribute --> <click selector="{{AdminProductFormSection.addAttributeBtn}}" stepKey="clickOnAddAttribute"/> <waitForPageLoad stepKey="waitForAttributePageToLoad"/> - <click selector="{{AdminProductAttributeGridSection.createNewAttributeBtn}}" stepKey="clickCreateNewAttributeButton"/> + <click selector="{{AdminProductFormSection.createNewAttributeBtn}}" stepKey="clickCreateNewAttributeButton"/> <waitForPageLoad stepKey="waitForNewAttributePageToLoad"/> <waitForElementVisible selector="{{AdminCreateNewProductAttributeSection.defaultLabel}}" stepKey="waitForDefaultLabelToBeVisible"/> <fillField selector="{{AdminCreateNewProductAttributeSection.defaultLabel}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillAttributeLabel"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml index 5badcc366ac3a..5c798db29b976 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeFromProductPageTest.xml @@ -59,7 +59,7 @@ <!-- Create New Product Attribute --> <click selector="{{AdminProductFormSection.addAttributeBtn}}" stepKey="clickOnAddAttribute"/> <waitForPageLoad stepKey="waitForAttributePageToLoad"/> - <click selector="{{AdminProductAttributeGridSection.createNewAttributeBtn}}" stepKey="clickCreateNewAttributeButton"/> + <click selector="{{AdminProductFormSection.createNewAttributeBtn}}" stepKey="clickCreateNewAttributeButton"/> <waitForPageLoad stepKey="waitForNewAttributePageToLoad"/> <waitForElementVisible selector="{{AdminCreateNewProductAttributeSection.defaultLabel}}" stepKey="waitForDefaultLabelToBeVisible"/> <fillField selector="{{AdminCreateNewProductAttributeSection.defaultLabel}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillAttributeLabel"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml index 176af624022e4..d4d6496e018f5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateProductAttributeRequiredTextFieldTest.xml @@ -57,7 +57,7 @@ <!-- Create Product Attribute --> <click selector="{{AdminProductFormSection.addAttributeBtn}}" stepKey="clickOnAddAttribute"/> <waitForPageLoad stepKey="waitForAttributePageToLoad"/> - <click selector="{{AdminProductAttributeGridSection.createNewAttributeBtn}}" stepKey="clickCreateNewAttributeButton"/> + <click selector="{{AdminProductFormSection.createNewAttributeBtn}}" stepKey="clickCreateNewAttributeButton"/> <waitForPageLoad stepKey="waitForNewAttributePageToLoad"/> <waitForElementVisible selector="{{AdminCreateNewProductAttributeSection.defaultLabel}}" stepKey="waitForDefaultLabelToBeVisible"/> <fillField selector="{{AdminCreateNewProductAttributeSection.defaultLabel}}" userInput="{{ProductAttributeFrontendLabel.label}}" stepKey="fillAttributeLabel"/> From 6e8277e5313aa9c40ec7c25c59f3f0394940cff1 Mon Sep 17 00:00:00 2001 From: John S <john00ivy@gmail.com> Date: Wed, 20 Feb 2019 09:48:31 -0600 Subject: [PATCH 147/254] MC-4526: Convert VerifyConfigurableProductLayeredNavigationTest to MFTF - Correcting an argument reference in an Action Group. - Replaced "waitForLoadingMaskToDisappear" with "waitForPageLoad". --- .../ActionGroup/StorefrontProductAttributeActionGroup.xml | 4 ++-- ...refrontVerifyConfigurableProductLayeredNavigationTest.xml | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductAttributeActionGroup.xml index 8fab65527315e..7780827381533 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductAttributeActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontProductAttributeActionGroup.xml @@ -14,7 +14,7 @@ <argument name="categoryName" type="string"/> <argument name="attributeDefaultLabel" type="string"/> </arguments> - <amOnPage url="categoryName" stepKey="openCategoryStoreFrontPage"/> + <amOnPage url="{{categoryName}}" stepKey="openCategoryStoreFrontPage"/> <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(categoryName)}}" stepKey="seeCategoryInFrontPage"/> <click selector="{{StorefrontHeaderSection.NavigationCategoryByName(categoryName)}}" stepKey="clickOnCategory"/> @@ -23,4 +23,4 @@ <click selector="{{StorefrontCategorySidebarSection.filterOptionsTitle(attributeDefaultLabel)}}" stepKey="clickAttributeOptions"/> <waitForPageLoad stepKey="waitForPageToLoad"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml index a230b6851da73..bb69122dc0be9 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml @@ -5,6 +5,7 @@ * See COPYING.txt for license details. */ --> + <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontVerifyConfigurableProductLayeredNavigationTest"> @@ -131,7 +132,7 @@ <scrollTo selector="{{AdminProductFormSection.productQuantity}}" stepKey="scrollToProductQuantity"/> <selectOption selector="{{AdminProductFormSection.productStockStatus}}" userInput="Out of Stock" stepKey="disableProduct"/> <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton"/> - <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the product." stepKey="messageYouSavedTheProductIsShown"/> <!--Open Category in Store Front and select product attribute option from sidebar --> @@ -145,4 +146,4 @@ <see selector="{{StorefrontCategorySidebarSection.filterOption}}" userInput="$$getConfigAttributeOption2.label$$" stepKey="seeOption2"/> <see selector="{{StorefrontCategorySidebarSection.filterOption}}" userInput="$$getConfigAttributeOption3.label$$" stepKey="seeOption3"/> </test> -</tests> \ No newline at end of file +</tests> From 72c39f18142583f6123ab92f2ea0d5c694f95a17 Mon Sep 17 00:00:00 2001 From: Alex Calandra <calandra.aj@gmail.com> Date: Wed, 20 Feb 2019 10:29:34 -0600 Subject: [PATCH 148/254] MC-4543: Convert MassAssignCustomerGroupTest to MFTF - Removing unused argument from action group reference --- .../Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml index 9948ba3cf6cfe..0cf6f986c4fa8 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml @@ -63,8 +63,6 @@ <group value="mtf_migrated"/> </annotations> <remove keyForRemoval="filterCustomer"/> - <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"> - <argument name="customerEmail" value="$customer.email$"/> - </actionGroup> + <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"/> </test> </tests> From 01bfe90b33206d352b14a80a2a2bdafafd90a7f9 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 20 Feb 2019 10:48:15 -0600 Subject: [PATCH 149/254] MAGETWO-98328: Update FedEx Shipping Dates behavior in Tracking Popup --- .../Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php index d148b91aa3e7a..86a576f2db650 100644 --- a/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php +++ b/app/code/Magento/Fedex/Plugin/Block/DataProviders/Tracking/ChangeTitle.php @@ -16,13 +16,15 @@ class ChangeTitle { /** + * Title modification in case if FedEx used as carrier + * * @param Subject $subject * @param \Magento\Framework\Phrase|string $result * @param Status $trackingStatus * @return \Magento\Framework\Phrase|string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterGetTitle(Subject $subject, $result, Status $trackingStatus): string + public function afterGetTitle(Subject $subject, $result, Status $trackingStatus) { if ($trackingStatus->getCarrier() === Carrier::CODE) { $result = __('Expected Delivery:'); From 0a56988e31ba69f2fb48500f98ee393fce0bf8d7 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 20 Feb 2019 11:07:31 -0600 Subject: [PATCH 150/254] MAGETWO-98328: Update FedEx Shipping Dates behavior in Tracking Popup --- .../Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php index dc66c4f0bd018..ec1ee277a5a51 100644 --- a/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php +++ b/app/code/Magento/Shipping/Block/DataProviders/Tracking/DeliveryDateTitle.php @@ -15,6 +15,8 @@ class DeliveryDateTitle implements ArgumentInterface { /** + * Returns Title in case if carrier defined + * * @param Status $trackingStatus * @return \Magento\Framework\Phrase|string */ From 9a0c504199f414bf8dfc9d286d6fda48f5fd208b Mon Sep 17 00:00:00 2001 From: Amol2jcommerce <amol@2jcommerce.in> Date: Wed, 20 Feb 2019 23:27:53 +0530 Subject: [PATCH 151/254] CSS-Property-name-issue --- .../Magento/blank/Magento_Swatches/web/css/source/_module.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/frontend/Magento/blank/Magento_Swatches/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Swatches/web/css/source/_module.less index 28aa3f187e95c..8824624d1c63a 100644 --- a/app/design/frontend/Magento/blank/Magento_Swatches/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Swatches/web/css/source/_module.less @@ -110,7 +110,7 @@ .lib-css(color, @attr-swatch-option__color); &.selected { - .lib-css(blackground, @attr-swatch-option__selected__background); + .lib-css(background, @attr-swatch-option__selected__background); .lib-css(border, @attr-swatch-option__selected__border); .lib-css(color, @attr-swatch-option__selected__color); } From a68e81575c158b92505cc5e3c300ca551fae5cf7 Mon Sep 17 00:00:00 2001 From: Amol2jcommerce <amol@2jcommerce.in> Date: Wed, 20 Feb 2019 23:33:33 +0530 Subject: [PATCH 152/254] unwanted changes reverted CSS-Property-name-issue --- .../Magento/blank/Magento_Customer/web/css/source/_module.less | 2 +- .../Magento/luma/Magento_Customer/web/css/source/_module.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 a94fedbcbbd14..9df59ca5dac92 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 @@ -421,7 +421,7 @@ > .field { > .control { - width: 80%; + width: 55%; } } } 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 61fc2610fc2e5..2e7856d390bd0 100755 --- 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 @@ -375,7 +375,7 @@ .fieldset { > .field { > .control { - width: 80%; + width: 55%; } } } From ecf6ddd231dff70ad5e2011b71ef9f8a2d3c0e94 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 20 Feb 2019 13:21:14 -0600 Subject: [PATCH 153/254] MAGETWO-98328: Update FedEx Shipping Dates behavior in Tracking Popup --- .../Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php b/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php index 942d21dfa5e47..e1597707f9d02 100644 --- a/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php +++ b/app/code/Magento/Fedex/Plugin/Block/Tracking/PopupDeliveryDate.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types=1); namespace Magento\Fedex\Plugin\Block\Tracking; @@ -26,7 +25,7 @@ class PopupDeliveryDate * @return string * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function afterFormatDeliveryDateTime(Popup $subject, string $result, string $date, string $time): string + public function afterFormatDeliveryDateTime(Popup $subject, $result, $date, $time) { if ($this->getCarrier($subject) === Carrier::CODE) { $result = $subject->formatDeliveryDate($date); From ec3162997f44ff03676ac4f1cd6e025c229d6352 Mon Sep 17 00:00:00 2001 From: John S <john00ivy@gmail.com> Date: Wed, 20 Feb 2019 14:33:31 -0600 Subject: [PATCH 154/254] MC-4543: Convert MassAssignCustomerGroupTest to MFTF - Fixing text alignment. - Adding setup/cleanup steps to the "before"/"after" blocks. - Fixing data references in the "actionGroups". --- .../Mftf/Test/ChangeCustomerGroupTest.xml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml index 9948ba3cf6cfe..b6b4ae667db78 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml @@ -6,7 +6,8 @@ */ --> -<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="ChangingSingleCustomerGroupViaGrid"> <annotations> <title value="Change a single customer group via grid"/> @@ -20,6 +21,8 @@ <before> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> <!--Delete created product--> @@ -28,30 +31,33 @@ <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> <argument name="customerGroupName" value="{{CustomerGroupChange.code}}"/> </actionGroup> + + <actionGroup ref="logout" stepKey="logout"/> </after> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <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="$createCustomer.fullname$"/> + <argument name="customerName" value="{{Simple_US_Customer.fullname}}"/> </actionGroup> <actionGroup ref="AdminSelectCustomerByEmail" stepKey="selectCustomer"> - <argument name="customerEmail" value="$createCustomer.email$"/> + <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="$createCustomer.fullname$"/> + <argument name="customerName" value="{{Simple_US_Customer.fullname}}"/> </actionGroup> <actionGroup ref="VerifyCustomerGroupForCustomer" stepKey="verifyCustomerGroupSet"> - <argument name="customerEmail" value="$createCustomer.email$"/> + <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"/> @@ -62,6 +68,7 @@ <group value="customer"/> <group value="mtf_migrated"/> </annotations> + <remove keyForRemoval="filterCustomer"/> <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"> <argument name="customerEmail" value="$customer.email$"/> From 139d4fa0676397aded13f512c28560655a31aca6 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 20 Feb 2019 15:10:43 -0600 Subject: [PATCH 155/254] MC-15020: Customer attribute of type file causes customer page to not load - Declared hidden dependency --- .../Magento/Ui/view/base/web/js/form/element/file-uploader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js index 357571350a268..c6bf36f5cc867 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/file-uploader.js @@ -16,7 +16,8 @@ define([ 'Magento_Ui/js/form/element/abstract', 'mage/backend/notification', 'mage/translate', - 'jquery/file-uploader' + 'jquery/file-uploader', + 'mage/adminhtml/tools' ], function ($, _, utils, uiAlert, validator, Element, notification, $t) { 'use strict'; From dc7d112ad0038aa022b94ab9e2fbb20ec1e571d3 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 20 Feb 2019 15:25:32 -0600 Subject: [PATCH 156/254] MC-13679: Forgot Password on Storefront validates customer email input - Fixed annotatations and seeInCurrentUrl --- .../Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml index acacc2c063f7c..12e603bd3748c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontForgotPasswordTest.xml @@ -17,7 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-13679"/> <group value="Customer"/> - <group value="mtf-migrated"/> + <group value="mtf_migrated"/> </annotations> <before> <magentoCLI command="config:set customer/captcha/enable 0" stepKey="disableCaptcha"/> @@ -34,7 +34,7 @@ <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}}"/> - <seeInUrl stepKey="seeInSignInPage" userInput="account/login"/> + <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> From 3fcc13cd385c531d3742b7ddbf550038ea5d0cae Mon Sep 17 00:00:00 2001 From: Serhii Dzhepa <sdzhepa@adobe.com> Date: Wed, 20 Feb 2019 15:34:43 -0600 Subject: [PATCH 157/254] Fixed test for case locator is overlaped --- .../Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml index 593bf95392633..537ffbd475b87 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml @@ -49,11 +49,11 @@ <amOnPage url="{{AdminSalesTaxClassPage.url}}" stepKey="navigateToSalesTaxPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" visible="false" stepKey="openTaxCalcSettingsSection"/> - <scrollTo selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" stepKey="goToCheckbox"/> + <scrollTo selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" x="0" y="-80" stepKey="goToCheckbox"/> <selectOption selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOn}}" userInput="{{userInput}}" stepKey="setApplyTaxOff"/> <checkOption selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" stepKey="disableApplyTaxOnSetting"/> <click selector="{{AdminConfigureTaxSection.save}}" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForConfigSaved"/> <see userInput="You saved the configuration." stepKey="seeSuccessMessage"/> </actionGroup> - </actionGroups> \ No newline at end of file + </actionGroups> From b9f9408349af2090e6f082cf52c7fe717bb052ec Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Wed, 20 Feb 2019 15:47:03 -0600 Subject: [PATCH 158/254] MC-10916: Update Customer Password on Storefront, Valid Current Password - Fix mtf_migrated annotation --- .../Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml index 38ea0de6f05a1..9bc253c91af92 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerPasswordTest.xml @@ -17,7 +17,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-10916"/> <group value="Customer"/> - <group value="mtf-migrated"/> + <group value="mtf_migrated"/> </annotations> <before> <createData stepKey="customer" entity="Simple_US_Customer"/> @@ -53,7 +53,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-10917"/> <group value="Customer"/> - <group value="mtf-migrated"/> + <group value="mtf_migrated"/> </annotations> <fillField stepKey="fillValidCurrentPassword" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.currentPassword}}"/> @@ -70,7 +70,7 @@ <severity value="CRITICAL"/> <testCaseId value="MC-10918"/> <group value="Customer"/> - <group value="mtf-migrated"/> + <group value="mtf_migrated"/> </annotations> <fillField stepKey="fillNewPasswordConfirmation" userInput="$$customer.password$$^" selector="{{StorefrontCustomerAccountInformationSection.confirmNewPassword}}"/> From b392c7cf48c78ad499ede0d80a67b6471908d703 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 20 Feb 2019 17:34:37 -0600 Subject: [PATCH 159/254] MC-15000: Add New Attribute on the admin product page with JS error - validation has be called as a function inside the component --- .../view/adminhtml/web/catalog/product/edit/attribute.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/edit/attribute.js b/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/edit/attribute.js index 407fd1fe28e39..e1923dc46d68e 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/edit/attribute.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/catalog/product/edit/attribute.js @@ -5,13 +5,13 @@ define([ 'jquery', - 'mage/mage' + 'mage/mage', + 'validation' ], function ($) { 'use strict'; return function (config, element) { - - $(element).mage('form').mage('validation', { + $(element).mage('form').validation({ validationUrl: config.validationUrl }); }; From c67c3fc8bf3d50bb25047b6be835ae99bd167914 Mon Sep 17 00:00:00 2001 From: Cristian Partica <cpartica@magento.com> Date: Wed, 20 Feb 2019 17:45:02 -0600 Subject: [PATCH 160/254] MC-15000: Add New Attribute on the admin product page with JS error - remove unused phtml file --- .../adminhtml/templates/class/page/edit.phtml | 20 ------------------- .../view/adminhtml/web/js/page/validate.js | 15 -------------- 2 files changed, 35 deletions(-) delete mode 100644 app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml delete mode 100644 app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js diff --git a/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml b/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml deleted file mode 100644 index 18e86549a1ff9..0000000000000 --- a/app/code/Magento/Tax/view/adminhtml/templates/class/page/edit.phtml +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -?> -<div data-mage-init='{"floatingHeader": {}}' class="page-actions"> - <?= $block->getBackButtonHtml() ?> - <?= $block->getResetButtonHtml() ?> - <?= $block->getDeleteButtonHtml() ?> - <?= $block->getSaveButtonHtml() ?> -</div> -<?= $block->getRenameFormHtml() ?> -<script type="text/x-magento-init"> - { - "#<?= /* @escapeNotVerified */ $block->getRenameFormId() ?>": { - "Magento_Tax/js/page/validate": {} - } - } -</script> diff --git a/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js b/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js deleted file mode 100644 index a49f199ba56b6..0000000000000 --- a/app/code/Magento/Tax/view/adminhtml/web/js/page/validate.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'mage/mage' -], function (jQuery) { - 'use strict'; - - return function (data, element) { - jQuery(element).mage('form').mage('validation'); - }; -}); From cfbbe0dfefafd701ad2d90bdf788020e285e898e Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Thu, 21 Feb 2019 10:21:42 +0200 Subject: [PATCH 161/254] Query Cart returns internal server error if configurable product was added --- app/code/Magento/QuoteGraphQl/etc/di.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/QuoteGraphQl/etc/di.xml b/app/code/Magento/QuoteGraphQl/etc/di.xml index ab9cd9da8d629..0697761a2a2a6 100644 --- a/app/code/Magento/QuoteGraphQl/etc/di.xml +++ b/app/code/Magento/QuoteGraphQl/etc/di.xml @@ -12,6 +12,7 @@ <argument name="supportedTypes" xsi:type="array"> <item name="simple" xsi:type="string">SimpleCartItem</item> <item name="virtual" xsi:type="string">VirtualCartItem</item> + <item name="configurable" xsi:type="string">ConfigurableCartItem</item> </argument> </arguments> </type> From 124b0e03bd3004b04f2efe3ecac4d758fc4891c5 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Thu, 21 Feb 2019 10:49:29 +0200 Subject: [PATCH 162/254] Fix static tests. --- app/code/Magento/Sales/Model/AdminOrder/Create.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 1ba5984799414..063433140566a 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -23,6 +23,7 @@ * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\Model\Cart\CartInterface From 6c3440124dbe4d206dc7c9b70719f3579b6a2038 Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Thu, 21 Feb 2019 17:34:31 +0200 Subject: [PATCH 163/254] graphQl-239: removed creating assets --- .../DataProvider/Image/Placeholder.php | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) 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 699613893949b..a7a9827a0be48 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 @@ -7,7 +7,6 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image; -use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Image\Placeholder\Theme; use Magento\Catalog\Model\View\Asset\PlaceholderFactory; use Magento\Framework\View\Asset\Repository as AssetRepository; @@ -26,25 +25,17 @@ class Placeholder */ private $assetRepository; - /** - * @var Theme - */ - private $theme; - /** * Placeholder constructor. * @param PlaceholderFactory $placeholderFactory * @param AssetRepository $assetRepository - * @param Theme $theme */ public function __construct( PlaceholderFactory $placeholderFactory, - AssetRepository $assetRepository, - Theme $theme + AssetRepository $assetRepository ) { $this->placeholderFactory = $placeholderFactory; $this->assetRepository = $assetRepository; - $this->theme = $theme; } /** @@ -52,7 +43,6 @@ public function __construct( * * @param string $imageType * @return string - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getPlaceholder(string $imageType): string { @@ -63,10 +53,8 @@ public function getPlaceholder(string $imageType): string return $imageAsset->getUrl(); } - $themeData = $this->theme->getThemeData(); - return $this->assetRepository->createAsset( - "Magento_Catalog::images/product/placeholder/{$imageType}.jpg", - $themeData - )->getUrl(); + return $this->assetRepository->getUrl( + "Magento_Catalog::images/product/placeholder/{$imageType}.jpg" + ); } } From 228ae14af1945dec3fd02356f6a87b5ab63dd3ca Mon Sep 17 00:00:00 2001 From: Vitaliy Boyko <v.boyko@atwix.com> Date: Thu, 21 Feb 2019 17:35:27 +0200 Subject: [PATCH 164/254] graphQl-239: removed unwanted doc --- .../Model/Resolver/Products/DataProvider/Image/Placeholder.php | 1 - 1 file changed, 1 deletion(-) 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 a7a9827a0be48..f5cf2a9ef82ff 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 @@ -26,7 +26,6 @@ class Placeholder private $assetRepository; /** - * Placeholder constructor. * @param PlaceholderFactory $placeholderFactory * @param AssetRepository $assetRepository */ From 8f3a1bd1d91ed2bbf27fa86a7fb17e17177544fd Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Thu, 21 Feb 2019 10:26:19 -0600 Subject: [PATCH 165/254] MQE-1453: Deliver weekly PR - Fix skip issueId in AdminCreateAndSwitchProductType --- .../Test/Mftf/Test/AdminCreateAndSwitchProductType.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml index d5f309b075727..e79f7f75cce3f 100755 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml @@ -54,7 +54,7 @@ <group value="catalog"/> <group value="mtf_migrated"/> <skip> - <issueId value="MQE-1445" /> + <issueId value="MAGETWO-62808"/> </skip> </annotations> <before> From 11b3a4f55e90d41cb8efa81672fda5ab3c32df4b Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Thu, 21 Feb 2019 11:16:45 -0600 Subject: [PATCH 166/254] GraphQL-292: Cleanup quote GraphQL scheme --- .../etc/schema.graphqls | 2 +- .../Magento/QuoteGraphQl/etc/schema.graphqls | 93 ++++++++----------- 2 files changed, 40 insertions(+), 55 deletions(-) diff --git a/app/code/Magento/ConfigurableProductGraphQl/etc/schema.graphqls b/app/code/Magento/ConfigurableProductGraphQl/etc/schema.graphqls index df95632c4b606..d4780c5c0867a 100644 --- a/app/code/Magento/ConfigurableProductGraphQl/etc/schema.graphqls +++ b/app/code/Magento/ConfigurableProductGraphQl/etc/schema.graphqls @@ -49,7 +49,7 @@ type AddConfigurableProductsToCartOutput { } input ConfigurableProductCartItemInput { - data: CartItemDetailsInput! + data: CartItemInput! variant_sku: String! customizable_options:[CustomizableOptionInput!] } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index f2806f6dd9f09..985b88b18d5c0 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -7,15 +7,48 @@ type Query { type Mutation { createEmptyCart: String @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CreateEmptyCart") @doc(description:"Creates an empty shopping cart for a guest or logged in user") - applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\ApplyCouponToCart") - removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\Coupon\\RemoveCouponFromCart") - setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingAddressesOnCart") + addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") + addVirtualProductsToCart(input: AddVirtualProductsToCartInput): AddVirtualProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveCouponFromCart") + setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingAddressesOnCart") setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetBillingAddressOnCart") setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart") - addSimpleProductsToCart(input: AddSimpleProductsToCartInput): AddSimpleProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") - addVirtualProductsToCart(input: AddVirtualProductsToCartInput): AddVirtualProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") +} + +input AddSimpleProductsToCartInput { + cart_id: String! + cartItems: [SimpleProductCartItemInput!]! +} + +input SimpleProductCartItemInput { + data: CartItemInput! + customizable_options:[CustomizableOptionInput!] +} + +input AddVirtualProductsToCartInput { + cart_id: String! + cartItems: [VirtualProductCartItemInput!]! +} + +input VirtualProductCartItemInput { + data: CartItemInput! + customizable_options:[CustomizableOptionInput!] +} + +input CartItemInput { + sku: String! + qty: Float! +} + +input CustomizableOptionInput { + id: Int! + value: String! +} + +input ApplyCouponToCartInput { + cart_id: String! + coupon_code: String! } input SetShippingAddressesOnCartInput { @@ -24,7 +57,7 @@ input SetShippingAddressesOnCartInput { } input ShippingAddressInput { - customer_address_id: Int # Can be provided in one-page checkout and is required for multi-shipping checkout + customer_address_id: Int # If provided then will be used address from address book address: CartAddressInput cart_items: [CartItemQuantityInput!] } @@ -81,19 +114,6 @@ type SetShippingMethodsOnCartOutput { cart: Cart! } -# If no address is provided, the system get address assigned to a quote -# If there's no address at all - the system returns all shipping methods -input AvailableShippingMethodsOnCartInput { - cart_id: String! - customer_address_id: Int - address: CartAddressInput -} - -input ApplyCouponToCartInput { - cart_id: String! - coupon_code: String! -} - type ApplyCouponToCartOutput { cart: Cart! } @@ -172,31 +192,6 @@ type RemoveCouponFromCartOutput { cart: Cart } -input AddSimpleProductsToCartInput { - cart_id: String! - cartItems: [SimpleProductCartItemInput!]! -} - -input AddVirtualProductsToCartInput { - cart_id: String! - cartItems: [VirtualProductCartItemInput!]! -} - -input SimpleProductCartItemInput { - data: CartItemInput! - customizable_options:[CustomizableOptionInput!] -} - -input VirtualProductCartItemInput { - data: CartItemInput! - customizable_options:[CustomizableOptionInput!] -} - -input CustomizableOptionInput { - id: Int! - value: String! -} - type AddSimpleProductsToCartOutput { cart: Cart! } @@ -213,11 +208,6 @@ type VirtualCartItem implements CartItemInterface @doc(description: "Virtual Car customizable_options: [SelectedCustomizableOption] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions") } -input CartItemInput { - sku: String! - qty: Float! -} - interface CartItemInterface @typeResolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CartItemTypeResolver") { id: String! qty: Float! @@ -246,8 +236,3 @@ type CartItemSelectedOptionValuePrice { units: String! type: PriceTypeEnum! } - -input CartItemDetailsInput { - sku: String! - qty: Float! -} From 628ee277f2b4ae01e23c78c06bb9f6460867615c Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Thu, 21 Feb 2019 13:31:58 -0600 Subject: [PATCH 167/254] MC-4905: Convert CreateCustomUrlRewriteEntityTest to MFTF --- .../ActionGroup/AdminCategoryActionGroup.xml | 12 +++ .../AdminProductGridActionGroup.xml | 16 ++++ .../AssertCMSPageContentActionGroup.xml | 10 +++ .../Mftf/Section/StorefrontCMSPageSection.xml | 1 + .../AdminUrlRewriteActionGroup.xml | 78 +++++++++++++++++ .../AdminUrlRewriteGridActionGroup.xml | 75 ++++++++++++++++ ...torefrontUrlRewriteRedirectActionGroup.xml | 32 +++++++ .../Mftf/Page/AdminUrlRewriteEditPage.xml | 14 +++ .../Mftf/Page/AdminUrlRewriteProductPage.xml | 14 +++ .../Section/AdminUrlRewriteIndexSection.xml | 2 + ...eUrlRewriteAndAddPermanentRedirectTest.xml | 86 +++++++++++++++++++ ...eUrlRewriteAndAddTemporaryRedirectTest.xml | 86 +++++++++++++++++++ ...yUrlRewriteAndAddPermanentRedirectTest.xml | 74 ++++++++++++++++ ...tUrlRewriteAndAddTemporaryRedirectTest.xml | 76 ++++++++++++++++ 14 files changed, 576 insertions(+) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteProductPage.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml index 84e8e43e83845..86986265bae2c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryActionGroup.xml @@ -263,4 +263,16 @@ <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessage"/> </actionGroup> + <actionGroup name="OpenCategoryFromCategoryTree"> + <arguments> + <argument name="category" type="string"/> + </arguments> + <amOnPage url="{{AdminCategoryPage.url}}" stepKey="openAdminCategoryIndexPage"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.expandAll}}" stepKey="clickOnExpandTree"/> + <waitForPageLoad stepKey="waitForCategoryToLoad"/> + <click selector="{{AdminCategorySidebarTreeSection.categoryInTree(category)}}" stepKey="selectCategory"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <waitForElementVisible selector="{{AdminCategoryContentSection.categoryPageTitle}}" stepKey="waitForCategoryTitle"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index f0367fb72c6a2..c9d70319c2877 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -272,4 +272,20 @@ <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> <waitForPageLoad stepKey="waitForGridLoad"/> </actionGroup> + <!--Filter and select the the product --> + <actionGroup name="filterAndSelectProduct"> + <arguments> + <argument name="productSku" type="string"/> + </arguments> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> + <waitForPageLoad stepKey="waitForProductIndexPageToLoad"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{productSku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad" time="30"/> + <click stepKey="openSelectedProduct" selector="{{AdminProductGridSection.productRowBySku(productSku)}}"/> + <waitForPageLoad stepKey="waitForProductToLoad"/> + <waitForElementVisible selector="{{AdminHeaderSection.pageTitle}}" stepKey="waitForProductTitle"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml index 58318660d2c42..dde6237390257 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageContentActionGroup.xml @@ -23,4 +23,14 @@ <executeJS function="(el = document.querySelector('[name=\'identifier\']')) && el['se' + 'tAt' + 'tribute']('data-value', el.value.split('-')[0]);" stepKey="setAttribute" /> <seeElement selector="{{CmsNewPagePageBasicFieldsSection.duplicatedURLKey(_duplicatedCMSPage.title)}}" stepKey="see"/> </actionGroup> + <actionGroup name="AssertStoreFrontCMSPage"> + <arguments> + <argument name="cmsTitle" type="string"/> + <argument name="cmsContent" type="string"/> + <argument name="cmsContentHeading" type="string"/> + </arguments> + <see selector="{{StorefrontCMSPageSection.title}}" userInput="{{cmsTitle}}" stepKey="seeTitle"/> + <see selector="{{StorefrontCMSPageSection.mainTitle}}" userInput="{{cmsContentHeading}}" stepKey="seeContentHeading"/> + <see selector="{{StorefrontCMSPageSection.mainContent}}" userInput="{{cmsContent}}" stepKey="seeContent"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml index 280c7dfd8263e..4ce8842c1ad87 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/StorefrontCMSPageSection.xml @@ -14,5 +14,6 @@ <element name="mainTitle" type="text" selector="#maincontent .page-title"/> <element name="mainContent" type="text" selector="#maincontent"/> <element name="footerTop" type="text" selector="footer.page-footer"/> + <element name="title" type="text" selector="//div[@class='breadcrumbs']//ul/li[@class='item cms_page']"/> </section> </sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..6bd38f53531bb --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteActionGroup.xml @@ -0,0 +1,78 @@ +<?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="AdminAddUrlRewrite"> + <arguments> + <argument name="category" type="string"/> + <argument name="customUrlRewriteValue" type="string"/> + <argument name="storeValue" type="string"/> + <argument name="requestPath" type="string"/> + <argument name="redirectTypeValue" type="string"/> + <argument name="description" type="string"/> + </arguments> + <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.name$$)}}" 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="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + </actionGroup> + <actionGroup name="AdminAddUrlRewriteForProduct"> + <arguments> + <argument name="storeValue" type="string"/> + <argument name="requestPath" type="string"/> + <argument name="redirectTypeValue" type="string"/> + <argument name="description" type="string"/> + </arguments> + <waitForElementVisible selector="{{AdminUrlRewriteProductSection.skipCategoryButton}}" stepKey="waitForSkipCategoryButton"/> + <click selector="{{AdminUrlRewriteProductSection.skipCategoryButton}}" stepKey="clickOnSkipCategoryButton"/> + <waitForPageLoad stepKey="waitForProductPageToLoad"/> + <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="{{AdminUrlRewriteIndexSection.successMessage}}" stepKey="seeSuccessSaveMessage"/> + </actionGroup> + <actionGroup name="AdminAddCustomUrlRewrite"> + <arguments> + <argument name="customUrlRewriteValue" type="string"/> + <argument name="storeValue" type="string"/> + <argument name="requestPath" type="string"/> + <argument name="targetPath" type="string"/> + <argument name="redirectTypeValue" type="string"/> + <argument name="description" type="string"/> + </arguments> + <amOnPage url="{{AdminUrlRewriteEditPage.url}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad" after="openUrlRewriteEditPage"/> + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewrite}}" stepKey="clickOnCustonUrlRewrite"/> + <click selector="{{AdminUrlRewriteEditSection.createCustomUrlRewriteValue('customUrlRewriteValue')}}" stepKey="selectCustom"/> + <click selector="{{AdminUrlRewriteEditSection.store}}" stepKey="clickOnStore"/> + <click selector="{{AdminUrlRewriteEditSection.storeValue('storeValue')}}" stepKey="clickOnStoreValue"/> + <fillField selector="{{AdminUrlRewriteEditSection.requestPath}}" userInput="{{requestPath}}" stepKey="fillRequestPath"/> + <fillField selector="{{AdminUrlRewriteEditSection.targetPath}}" userInput="{{targetPath}}" stepKey="fillTargetPath"/> + <click selector="{{AdminUrlRewriteEditSection.redirectType}}" stepKey="selectRedirectType"/> + <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"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml new file mode 100644 index 0000000000000..f053d18e79c3e --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminUrlRewriteGridActionGroup.xml @@ -0,0 +1,75 @@ +<?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="AdminSearchByRequestPath"> + <arguments> + <argument name="redirectPath" type="string"/> + <argument name="redirectType" type="string"/> + <argument name="targetPath" type="string"/> + </arguments> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> + <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{redirectPath}}" stepKey="fillRedirectPathFilter"/> + <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <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" /> + </actionGroup> + <actionGroup name="AdminSearchProductBySku"> + <arguments> + <argument name="productSku" type="string"/> + </arguments> + <amOnPage url="{{AdminUrlRewriteProductPage.url}}" stepKey="openUrlRewriteProductPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteProductPageToLoad"/> + <click selector="{{AdminUrlRewriteProductSection.resetFilter}}" stepKey="clickOnResetFilter"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminUrlRewriteProductSection.skuFilter}}" userInput="{{productSku}}" stepKey="fillProductSkuFilter"/> + <click selector="{{AdminUrlRewriteProductSection.searchFilter}}" stepKey="clickOnSearchFilter"/> + <waitForPageLoad stepKey="waitForProductToLoad"/> + <click selector="{{AdminUrlRewriteProductSection.productRow}}" stepKey="clickOnFirstRow"/> + <waitForPageLoad stepKey="waitForProductCategoryPageToLoad"/> + </actionGroup> + <actionGroup name="AdminSearchDeletedUrlRewrite"> + <arguments> + <argument name="requestPath" type="string"/> + </arguments> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> + <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{requestPath}}" stepKey="fillRedirectPathFilter"/> + <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <see selector="{{AdminUrlRewriteIndexSection.emptyRecords}}" userInput="We couldn't find any records." stepKey="seeEmptyRecordMessage"/> + </actionGroup> + <actionGroup name="AdminDeleteUrlRewrite"> + <arguments> + <argument name="requestPath" type="string"/> + </arguments> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="openUrlRewriteEditPage"/> + <waitForPageLoad stepKey="waitForUrlRewriteEditPageToLoad"/> + <click selector="{{AdminUrlRewriteIndexSection.resetButton}}" stepKey="clickOnResetButton"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="{{requestPath}}" stepKey="fillRedirectPathFilter"/> + <click selector="{{AdminUrlRewriteIndexSection.searchButton}}" stepKey="clickOnSearchButton"/> + <waitForPageLoad stepKey="waitForPageToLoad1"/> + <click selector="{{AdminUrlRewriteIndexSection.editButton('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"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml new file mode 100644 index 0000000000000..cdbd3d146a5b2 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.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="StorefrontUrlRewriteRedirect"> + <arguments> + <argument name="category" type="string"/> + <argument name="newRequestPath" type="string"/> + </arguments> + <amOnPage url="{{newRequestPath}}" stepKey="openCategoryInStorefront"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(category)}}" stepKey="seeCategoryOnStoreNavigationBar"/> + <seeElement selector="{{StorefrontCategoryMainSection.CategoryTitle(category)}}" stepKey="seeCategoryInTitle"/> + </actionGroup> + <actionGroup name="StorefrontProductRedirect"> + <arguments> + <argument name="productName" type="string"/> + <argument name="productSku" type="string"/> + <argument name="productRequestPath" type="string"/> + </arguments> + <amOnPage url="{{productRequestPath}}" stepKey="openCategoryInStorefront"/> + <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{productName}}" stepKey="seeProductNameInStoreFrontPage"/> + <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{productSku}}" stepKey="seeProductSkuInStoreFrontPage"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.xml new file mode 100644 index 0000000000000..d8a21b1be8ad7 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteEditPage.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="AdminUrlRewriteEditPage" url="admin/url_rewrite/edit/id" area="admin" module="Magento_UrlRewrite"> + <section name="AdminUrlRewriteEditSection"/> + </page> +</pages> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteProductPage.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteProductPage.xml new file mode 100644 index 0000000000000..645396bc778e9 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Page/AdminUrlRewriteProductPage.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="AdminUrlRewriteProductPage" url="admin/url_rewrite/edit/product" area="admin" module="Magento_UrlRewrite"> + <section name="AdminUrlRewriteProductSection"/> + </page> +</pages> \ No newline at end of file diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml index 7c21acdf943ba..ef86b2eb7223a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml @@ -19,5 +19,7 @@ <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'][{{var1}}]/td/a[contains(.,'Edit')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.xml new file mode 100644 index 0000000000000..358aa58aba0f7 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest.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="AdminCreateCustomCMSPageUrlRewriteAndAddPermanentRedirectTest"> + <annotations> + <stories value="Create custom URL rewrite"/> + <title value="Create custom URL rewrite, CMS permanent"/> + <description value="Login as Admin and create custom CMS page UrlRewrite and add Permanent redirect type "/> + <testCaseId value="MC-5345"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Open CMS Edit Page and Get the CMS ID --> + <actionGroup ref="navigateToCreatedCMSPage" 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="AdminAddCustomUrlRewrite" 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="AdminSearchByRequestPath" 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="AdminSearchByRequestPath" 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="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront"> + <argument name="page" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!-- Assert updated CMS redirect in Store Front --> + <actionGroup ref="AssertStoreFrontCMSPage" 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="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront1"> + <argument name="page" value="$$createCMSPage.identifier$$"/> + </actionGroup> + + <!-- Assert initial CMS redirect in Store Front --> + <actionGroup ref="AssertStoreFrontCMSPage" stepKey="assertCMSPage1"> + <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/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.xml new file mode 100644 index 0000000000000..e6ee9b484059d --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest.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="AdminCreateCustomCMSPageUrlRewriteAndAddTemporaryRedirectTest"> + <annotations> + <stories value="Create custom URL rewrite"/> + <title value="Create custom URL rewrite, CMS temporary"/> + <description value="Login as Admin and create custom CMS page UrlRewrite and add Temporary redirect type "/> + <testCaseId value="MC-5346"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="simpleCmsPage" stepKey="createCMSPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData createDataKey="createCMSPage" stepKey="deleteCMSPage"/> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="{{defaultCmsPage.title}}"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Open CMS Edit Page and Get the CMS ID --> + <actionGroup ref="navigateToCreatedCMSPage" 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="AdminAddCustomUrlRewrite" 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="AdminSearchByRequestPath" 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="AdminSearchByRequestPath" 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="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront"> + <argument name="page" value="{{defaultCmsPage.title}}"/> + </actionGroup> + + <!--Assert updated CMS redirect in Store Front--> + <actionGroup ref="AssertStoreFrontCMSPage" 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="navigateToStorefrontForCreatedPage" stepKey="navigateToTheStoreFront1"> + <argument name="page" value="$$createCMSPage.identifier$$"/> + </actionGroup> + + <!--Assert initial CMS redirect in Store Front--> + <actionGroup ref="AssertStoreFrontCMSPage" stepKey="assertCMSPage1"> + <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/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml new file mode 100644 index 0000000000000..64bb6c5fa13c0 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.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="AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest"> + <annotations> + <stories value="Create custom URL rewrite"/> + <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"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="SimpleSubCategory" stepKey="category"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="{{FirstLevelSubCat.name}}.html"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Open Category Page and Get Category ID --> + <actionGroup ref="OpenCategoryFromCategoryTree" stepKey="getCategoryId"> + <argument name="category" value="$$category.name$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="categoryId" regex="#\/([0-9]*)?\/$#"/> + + <!-- Open UrlRewrite Edit page and update the fields and fill the created category Target Path --> + <actionGroup ref="AdminAddCustomUrlRewrite" stepKey="addCustomUrlRewrite"> + <argument name="customUrlRewriteValue" value="Custom"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="{{FirstLevelSubCat.name}}.html"/> + <argument name="redirectTypeValue" value="Permanent (301)"/> + <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!-- Assert updated category Url Rewrite in grid --> + <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByCategoryRequestPath"> + <argument name="redirectPath" value="$$category.name$$.html" /> + <argument name="redirectType" value="No" /> + <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> + </actionGroup> + + <!--Assert initial category Url Rewrite in grid --> + <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByNewRequestPath"> + <argument name="redirectPath" value="{{FirstLevelSubCat.name}}.html" /> + <argument name="redirectType" value="Permanent (301)" /> + <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> + </actionGroup> + + <!-- Assert updated Category redirect in Store Front --> + <actionGroup ref="StorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront"> + <argument name="category" value="$$category.name$$"/> + <argument name="newRequestPath" value="{{FirstLevelSubCat.name}}.html"/> + </actionGroup> + + <!-- Assert initial Category redirect in Store Front --> + <actionGroup ref="StorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront1"> + <argument name="category" value="$$category.name$$"/> + <argument name="newRequestPath" value="catalog/category/view/id/{$categoryId}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml new file mode 100644 index 0000000000000..2086f16cd4f7b --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.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="AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest"> + <annotations> + <stories value="Create custom URL rewrite"/> + <title value="Create custom URL rewrite, temporary"/> + <description value="Login as Admin and create custom product UrlRewrite and add Temporary redirect type "/> + <testCaseId value="MC-5344"/> + <severity value="CRITICAL"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <createData entity="defaultSimpleProduct" stepKey="createProduct"/> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="AdminDeleteUrlRewrite" stepKey="deleteCustomUrlRewrite"> + <argument name="requestPath" value="{{_defaultProduct.name}}.html"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Filter Product in product page and get the Product ID --> + <actionGroup ref="filterAndSelectProduct" stepKey="filterProduct"> + <argument name="productSku" value="$$createProduct.sku$$"/> + </actionGroup> + <grabFromCurrentUrl stepKey="productId" regex="#\/([0-9]*)?\/$#"/> + + <!-- Open UrlRewrite Edit page and update the fields and fill the created product Target Path --> + <actionGroup ref="AdminAddCustomUrlRewrite" stepKey="addCustomUrlRewrite"> + <argument name="customUrlRewriteValue" value="Custom"/> + <argument name="storeValue" value="Default Store View"/> + <argument name="requestPath" value="{{_defaultProduct.name}}.html"/> + <argument name="redirectTypeValue" value="Temporary (302)"/> + <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> + <argument name="description" value="End To End Test"/> + </actionGroup> + + <!--Assert updated product Url Rewrite in Grid --> + <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath"> + <argument name="redirectPath" value="{{_defaultProduct.name}}.html" /> + <argument name="redirectType" value="Temporary (302)" /> + <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> + </actionGroup> + + <!-- Assert initial product Url rewrite in grid --> + <actionGroup ref="AdminSearchByRequestPath" stepKey="searchByRequestPath1"> + <argument name="redirectPath" value="$$createProduct.name$$.html" /> + <argument name="redirectType" value="No"/> + <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> + </actionGroup> + + <!-- Assert updated product redirect in Store Front--> + <actionGroup ref="StorefrontProductRedirect" stepKey="verifyProductInStoreFront"> + <argument name="productName" value="$$createProduct.name$$"/> + <argument name="productSku" value="$$createProduct.sku$$"/> + <argument name="productRequestPath" value="{{_defaultProduct.name}}.html"/> + </actionGroup> + + <!-- Assert initial product redirect in Store Front--> + <actionGroup ref="StorefrontProductRedirect" stepKey="verifyProductInStoreFront1"> + <argument name="productName" value="$$createProduct.name$$"/> + <argument name="productSku" value="$$createProduct.sku$$"/> + <argument name="productRequestPath" value="$$createProduct.name$$.html"/> + </actionGroup> + </test> +</tests> From 5d3870a57594d91884dd80d312983c840780849b Mon Sep 17 00:00:00 2001 From: Iryna Lagno <ilagno@adobe.com> Date: Thu, 21 Feb 2019 14:03:56 -0600 Subject: [PATCH 168/254] MC-15011: Text is not displayed correctly --- .../Cms/Test/Mftf/Page/CmsPageEditPage.xml | 2 +- .../AdminProductFormConfigurationsSection.xml | 1 + .../AdminConfigurableProductCreateTest.xml | 1 + .../Product/Form/Modifier/Links.php | 26 +++++++++++++++++-- .../Product/Form/Modifier/Samples.php | 15 +++++++++-- .../form/element/uploader/uploader.html | 2 +- .../backend/web/css/source/forms/_fields.less | 1 + 7 files changed, 42 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml index 885310d9399ae..73db6b61343b1 100644 --- a/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.xml +++ b/app/code/Magento/Cms/Test/Mftf/Page/CmsPageEditPage.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="CmsPageEditPage" area="admin" url="admin/cms_page/edit/page_id/{{var}}" parameterized="true"> + <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"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml index c5d6abd89edbf..73ae71adfaaf0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml @@ -27,6 +27,7 @@ <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="variationLabel" type="text" selector="//div[@data-index='configurable-matrix']/label"/> </section> <section name="AdminConfigurableProductFormSection"> <element name="productWeight" type="input" selector=".admin__control-text[name='product[weight]']"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml index 24af7d44e8261..2af85e1bac048 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml @@ -36,6 +36,7 @@ </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"/> 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 a352c4bdf7bc3..2188a671a5aa0 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 @@ -86,7 +86,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -101,7 +101,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function modifyMeta(array $meta) @@ -160,6 +160,8 @@ public function modifyMeta(array $meta) } /** + * Returns configuration for dynamic rows + * * @return array */ protected function getDynamicRows() @@ -180,6 +182,8 @@ protected function getDynamicRows() } /** + * Returns Record column configuration + * * @return array */ protected function getRecord() @@ -221,6 +225,8 @@ protected function getRecord() } /** + * Returns Title column configuration + * * @return array */ protected function getTitleColumn() @@ -238,6 +244,7 @@ protected function getTitleColumn() 'componentType' => Form\Field::NAME, 'dataType' => Form\Element\DataType\Text::NAME, 'dataScope' => 'title', + 'labelVisible' => false, 'validation' => [ 'required-entry' => true, ], @@ -247,6 +254,8 @@ protected function getTitleColumn() } /** + * Returns Price column configuration + * * @return array */ protected function getPriceColumn() @@ -265,6 +274,7 @@ protected function getPriceColumn() 'dataType' => Form\Element\DataType\Number::NAME, 'component' => 'Magento_Downloadable/js/components/price-handler', 'dataScope' => 'price', + 'labelVisible' => false, 'addbefore' => $this->locator->getStore()->getBaseCurrency() ->getCurrencySymbol(), 'validation' => [ @@ -281,6 +291,8 @@ protected function getPriceColumn() } /** + * Returns File column configuration + * * @return array */ protected function getFileColumn() @@ -302,6 +314,7 @@ protected function getFileColumn() 'options' => $this->typeUpload->toOptionArray(), 'typeFile' => 'links_file', 'typeUrl' => 'link_url', + 'labelVisible' => false, ]; $fileLinkUrl['arguments']['data']['config'] = [ 'formElement' => Form\Element\Input::NAME, @@ -344,6 +357,8 @@ protected function getFileColumn() } /** + * Returns Sample column configuration + * * @return array */ protected function getSampleColumn() @@ -363,6 +378,7 @@ protected function getSampleColumn() 'dataType' => Form\Element\DataType\Text::NAME, 'dataScope' => 'sample.type', 'options' => $this->typeUpload->toOptionArray(), + 'labelVisible' => false, 'typeFile' => 'sample_file', 'typeUrl' => 'sample_url', ]; @@ -382,6 +398,7 @@ protected function getSampleColumn() 'component' => 'Magento_Downloadable/js/components/file-uploader', 'elementTmpl' => 'Magento_Downloadable/components/file-uploader', 'fileInputName' => 'link_samples', + 'labelVisible' => false, 'uploaderConfig' => [ 'url' => $this->urlBuilder->addSessionParam()->getUrl( 'adminhtml/downloadable_file/upload', @@ -403,6 +420,8 @@ protected function getSampleColumn() } /** + * Returns Sharable columns configuration + * * @return array */ protected function getShareableColumn() @@ -420,6 +439,8 @@ protected function getShareableColumn() } /** + * Returns max downloads column configuration + * * @return array */ protected function getMaxDownloadsColumn() @@ -437,6 +458,7 @@ protected function getMaxDownloadsColumn() 'componentType' => Form\Field::NAME, 'dataType' => Form\Element\DataType\Number::NAME, 'dataScope' => 'number_of_downloads', + 'labelVisible' => false, 'value' => 0, 'validation' => [ 'validate-zero-or-greater' => 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 1587163ba8121..197bf1338f945 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 @@ -77,7 +77,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -90,7 +90,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function modifyMeta(array $meta) @@ -135,6 +135,8 @@ public function modifyMeta(array $meta) } /** + * Returns configuration for dynamic rows + * * @return array */ protected function getDynamicRows() @@ -155,6 +157,8 @@ protected function getDynamicRows() } /** + * Returns Record column configuration + * * @return array */ protected function getRecord() @@ -192,6 +196,8 @@ protected function getRecord() } /** + * Returns Title column configuration + * * @return array */ protected function getTitleColumn() @@ -209,6 +215,7 @@ protected function getTitleColumn() 'componentType' => Form\Field::NAME, 'dataType' => Form\Element\DataType\Text::NAME, 'dataScope' => 'title', + 'labelVisible' => false, 'validation' => [ 'required-entry' => true, ], @@ -218,6 +225,8 @@ protected function getTitleColumn() } /** + * Returns Sample column configuration + * * @return array */ protected function getSampleColumn() @@ -236,6 +245,7 @@ protected function getSampleColumn() 'component' => 'Magento_Downloadable/js/components/upload-type-handler', 'dataType' => Form\Element\DataType\Text::NAME, 'dataScope' => 'type', + 'labelVisible' => false, 'options' => $this->typeUpload->toOptionArray(), 'typeFile' => 'sample_file', 'typeUrl' => 'sample_url', @@ -246,6 +256,7 @@ protected function getSampleColumn() 'dataType' => Form\Element\DataType\Text::NAME, 'dataScope' => 'sample_url', 'placeholder' => 'URL', + 'labelVisible' => false, 'validation' => [ 'required-entry' => true, 'validate-url' => true, diff --git a/app/code/Magento/Ui/view/base/web/templates/form/element/uploader/uploader.html b/app/code/Magento/Ui/view/base/web/templates/form/element/uploader/uploader.html index a92b85cb47401..cf4e2243b5886 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/element/uploader/uploader.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/element/uploader/uploader.html @@ -6,7 +6,7 @@ --> <div class="admin__field" visible="visible" css="$data.additionalClasses"> - <label class="admin__field-label" if="$data.label" attr="for: uid"> + <label class="admin__field-label" if="$data.label" attr="for: uid" visible="$data.labelVisible"> <span translate="label" attr="'data-config-scope': $data.scopeLabel"/> </label> 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 4b77c518d3df8..13453c58f5326 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 @@ -546,6 +546,7 @@ & > .admin__field-label { #mix-grid .column(@field-label-grid__column, @field-grid__columns); cursor: pointer; + background: @color-white; left: 0; position: absolute; top: 0; From 03429a30989f97fe8ce673c21b06612648eab98b Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Thu, 21 Feb 2019 08:52:17 -0600 Subject: [PATCH 169/254] MC-4538: Convert RegisterCustomerFrontendEntityTest to MFTF - Addressing review comments --- .../SignUpNewUserFromStorefrontActionGroup.xml | 8 +++++++- .../Test/AdminCreateNewCustomerOnStorefrontTest.xml | 10 ++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml index 323dbc2bd5b5f..4d447d1f43358 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -40,7 +40,13 @@ <fillField stepKey="fillConfirmPassword" userInput="{{customer.password}}" selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}"/> <click stepKey="clickCreateAccountButton" selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}"/> <waitForPageLoad stepKey="waitForCreateAccountButtonToLoad" /> - <see stepKey="seeThankYouMessage" userInput="Thank you for registering with Main Website Store."/> + </actionGroup> + <actionGroup name="AssertSignedUpNewsletterActionGroup"> + <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}}" /> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml index 11f2e83f43be9..057590cc3fc39 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml @@ -29,10 +29,12 @@ <actionGroup ref="logout" stepKey="logout"/> </after> - <!--Create new customer on storefront--> - <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="SignUpNewUser"> - <argument name="Customer" value="CustomerEntityOne"/> + <!--Create new customer on storefront and perform the asserts--> + <actionGroup ref="SignUpNewUserFromStorefrontActionGroup" stepKey="signUpNewUser"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <actionGroup ref="AssertSignedUpNewsletterActionGroup" stepKey="assertSignedUpNewsLetter"> + <argument name="customer" value="CustomerEntityOne"/> </actionGroup> - <seeInCurrentUrl url="{{StorefrontCustomerDashboardPage.url}}" stepKey="seeAssertInCurrentUrl"/> </test> </tests> \ No newline at end of file From 364ebfe5c1092b2c29b6eb5d5f36d2b8edec56a7 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Thu, 21 Feb 2019 15:00:25 -0600 Subject: [PATCH 170/254] MC-15004: Indexer hangs on category/products index - Changed algorithm of batching for affected indexers - Cleaned up touched files --- .../Indexer/Category/Product/Action/Full.php | 142 +++++----- .../Model/Indexer/Product/Eav/Action/Full.php | 112 +++++--- .../Indexer/Product/Price/Action/Full.php | 246 ++++++++++-------- .../Indexer/Product/Eav/Action/FullTest.php | 73 ++++-- .../Model/Indexer/Stock/Action/Full.php | 55 ++-- 5 files changed, 369 insertions(+), 259 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php index f8121b55dbf99..e7b1e2c9bd62f 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php @@ -3,33 +3,46 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Category\Product\Action; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Config; +use Magento\Catalog\Model\Indexer\Category\Product\AbstractAction; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Query\Generator as QueryGenerator; use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Select; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Indexer\BatchProviderInterface; +use Magento\Framework\Indexer\BatchSizeManagementInterface; use Magento\Indexer\Model\ProcessManager; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; /** * Class Full reindex action * - * @package Magento\Catalog\Model\Indexer\Category\Product\Action * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractAction +class Full extends AbstractAction { /** - * @var \Magento\Framework\Indexer\BatchSizeManagementInterface + * @var BatchSizeManagementInterface */ private $batchSizeManagement; /** - * @var \Magento\Framework\Indexer\BatchProviderInterface + * @var BatchProviderInterface */ private $batchProvider; /** - * @var \Magento\Framework\EntityManager\MetadataPool + * @var MetadataPool */ protected $metadataPool; @@ -52,25 +65,25 @@ class Full extends \Magento\Catalog\Model\Indexer\Category\Product\AbstractActio /** * @param ResourceConnection $resource - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Catalog\Model\Config $config + * @param StoreManagerInterface $storeManager + * @param Config $config * @param QueryGenerator|null $queryGenerator - * @param \Magento\Framework\Indexer\BatchSizeManagementInterface|null $batchSizeManagement - * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider - * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool + * @param BatchSizeManagementInterface|null $batchSizeManagement + * @param BatchProviderInterface|null $batchProvider + * @param MetadataPool|null $metadataPool * @param int|null $batchRowsCount * @param ActiveTableSwitcher|null $activeTableSwitcher * @param ProcessManager $processManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\App\ResourceConnection $resource, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Catalog\Model\Config $config, + ResourceConnection $resource, + StoreManagerInterface $storeManager, + Config $config, QueryGenerator $queryGenerator = null, - \Magento\Framework\Indexer\BatchSizeManagementInterface $batchSizeManagement = null, - \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, - \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, + BatchSizeManagementInterface $batchSizeManagement = null, + BatchProviderInterface $batchProvider = null, + MetadataPool $metadataPool = null, $batchRowsCount = null, ActiveTableSwitcher $activeTableSwitcher = null, ProcessManager $processManager = null @@ -81,15 +94,15 @@ public function __construct( $config, $queryGenerator ); - $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); + $objectManager = ObjectManager::getInstance(); $this->batchSizeManagement = $batchSizeManagement ?: $objectManager->get( - \Magento\Framework\Indexer\BatchSizeManagementInterface::class + BatchSizeManagementInterface::class ); $this->batchProvider = $batchProvider ?: $objectManager->get( - \Magento\Framework\Indexer\BatchProviderInterface::class + BatchProviderInterface::class ); $this->metadataPool = $metadataPool ?: $objectManager->get( - \Magento\Framework\EntityManager\MetadataPool::class + MetadataPool::class ); $this->batchRowsCount = $batchRowsCount; $this->activeTableSwitcher = $activeTableSwitcher ?: $objectManager->get(ActiveTableSwitcher::class); @@ -97,33 +110,39 @@ public function __construct( } /** + * Creates the store tables + * * @return void */ - private function createTables() + private function createTables(): void { foreach ($this->storeManager->getStores() as $store) { - $this->tableMaintainer->createTablesForStore($store->getId()); + $this->tableMaintainer->createTablesForStore((int)$store->getId()); } } /** + * Truncates the replica tables + * * @return void */ - private function clearReplicaTables() + private function clearReplicaTables(): void { foreach ($this->storeManager->getStores() as $store) { - $this->connection->truncateTable($this->tableMaintainer->getMainReplicaTable($store->getId())); + $this->connection->truncateTable($this->tableMaintainer->getMainReplicaTable((int)$store->getId())); } } /** + * Switches the active table + * * @return void */ - private function switchTables() + private function switchTables(): void { $tablesToSwitch = []; foreach ($this->storeManager->getStores() as $store) { - $tablesToSwitch[] = $this->tableMaintainer->getMainTable($store->getId()); + $tablesToSwitch[] = $this->tableMaintainer->getMainTable((int)$store->getId()); } $this->activeTableSwitcher->switchTable($this->connection, $tablesToSwitch); } @@ -133,12 +152,13 @@ private function switchTables() * * @return $this */ - public function execute() + public function execute(): self { $this->createTables(); $this->clearReplicaTables(); $this->reindex(); $this->switchTables(); + return $this; } @@ -147,7 +167,7 @@ public function execute() * * @return void */ - protected function reindex() + protected function reindex(): void { $userFunctions = []; @@ -165,9 +185,9 @@ protected function reindex() /** * Execute indexation by store * - * @param \Magento\Store\Model\Store $store + * @param Store $store */ - private function reindexStore($store) + private function reindexStore($store): void { $this->reindexRootCategory($store); $this->reindexAnchorCategories($store); @@ -177,31 +197,31 @@ private function reindexStore($store) /** * Publish data from tmp to replica table * - * @param \Magento\Store\Model\Store $store + * @param Store $store * @return void */ - private function publishData($store) + private function publishData($store): void { - $select = $this->connection->select()->from($this->tableMaintainer->getMainTmpTable($store->getId())); + $select = $this->connection->select()->from($this->tableMaintainer->getMainTmpTable((int)$store->getId())); $columns = array_keys( - $this->connection->describeTable($this->tableMaintainer->getMainReplicaTable($store->getId())) + $this->connection->describeTable($this->tableMaintainer->getMainReplicaTable((int)$store->getId())) ); - $tableName = $this->tableMaintainer->getMainReplicaTable($store->getId()); + $tableName = $this->tableMaintainer->getMainReplicaTable((int)$store->getId()); $this->connection->query( $this->connection->insertFromSelect( $select, $tableName, $columns, - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + AdapterInterface::INSERT_ON_DUPLICATE ) ); } /** - * {@inheritdoc} + * @inheritdoc */ - protected function reindexRootCategory(\Magento\Store\Model\Store $store) + protected function reindexRootCategory(Store $store): void { if ($this->isIndexRootCategoryNeeded()) { $this->reindexCategoriesBySelect($this->getAllProducts($store), 'cp.entity_id IN (?)', $store); @@ -211,10 +231,10 @@ protected function reindexRootCategory(\Magento\Store\Model\Store $store) /** * Reindex products of anchor categories * - * @param \Magento\Store\Model\Store $store + * @param Store $store * @return void */ - protected function reindexAnchorCategories(\Magento\Store\Model\Store $store) + protected function reindexAnchorCategories(Store $store): void { $this->reindexCategoriesBySelect($this->getAnchorCategoriesSelect($store), 'ccp.product_id IN (?)', $store); } @@ -222,10 +242,10 @@ protected function reindexAnchorCategories(\Magento\Store\Model\Store $store) /** * Reindex products of non anchor categories * - * @param \Magento\Store\Model\Store $store + * @param Store $store * @return void */ - protected function reindexNonAnchorCategories(\Magento\Store\Model\Store $store) + protected function reindexNonAnchorCategories(Store $store): void { $this->reindexCategoriesBySelect($this->getNonAnchorCategoriesSelect($store), 'ccp.product_id IN (?)', $store); } @@ -233,40 +253,42 @@ protected function reindexNonAnchorCategories(\Magento\Store\Model\Store $store) /** * Reindex categories using given SQL select and condition. * - * @param \Magento\Framework\DB\Select $basicSelect + * @param Select $basicSelect * @param string $whereCondition - * @param \Magento\Store\Model\Store $store + * @param Store $store * @return void */ - private function reindexCategoriesBySelect(\Magento\Framework\DB\Select $basicSelect, $whereCondition, $store) + private function reindexCategoriesBySelect(Select $basicSelect, $whereCondition, $store): void { - $this->tableMaintainer->createMainTmpTable($store->getId()); + $this->tableMaintainer->createMainTmpTable((int)$store->getId()); - $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + $entityMetadata = $this->metadataPool->getMetadata(ProductInterface::class); $columns = array_keys( - $this->connection->describeTable($this->tableMaintainer->getMainTmpTable($store->getId())) + $this->connection->describeTable($this->tableMaintainer->getMainTmpTable((int)$store->getId())) ); $this->batchSizeManagement->ensureBatchSize($this->connection, $this->batchRowsCount); - $batches = $this->batchProvider->getBatches( - $this->connection, - $entityMetadata->getEntityTable(), + + $select = $this->connection->select(); + $select->distinct(true); + $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); + + $batchQueries = $this->prepareSelectsByRange( + $select, $entityMetadata->getIdentifierField(), - $this->batchRowsCount + (int)$this->batchRowsCount ); - foreach ($batches as $batch) { - $this->connection->delete($this->tableMaintainer->getMainTmpTable($store->getId())); + + foreach ($batchQueries as $query) { + $this->connection->delete($this->tableMaintainer->getMainTmpTable((int)$store->getId())); + $entityIds = $this->connection->fetchCol($query); $resultSelect = clone $basicSelect; - $select = $this->connection->select(); - $select->distinct(true); - $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); - $entityIds = $this->batchProvider->getBatchIds($this->connection, $select, $batch); $resultSelect->where($whereCondition, $entityIds); $this->connection->query( $this->connection->insertFromSelect( $resultSelect, - $this->tableMaintainer->getMainTmpTable($store->getId()), + $this->tableMaintainer->getMainTmpTable((int)$store->getId()), $columns, - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + AdapterInterface::INSERT_ON_DUPLICATE ) ); $this->publishData($store); diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php index 802176092d147..ed8f692885d91 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Eav/Action/Full.php @@ -7,26 +7,41 @@ namespace Magento\Catalog\Model\Indexer\Product\Eav\Action; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Query\BatchIteratorInterface; +use Magento\Framework\DB\Query\Generator as QueryGenerator; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Indexer\BatchProviderInterface; +use Magento\Store\Model\ScopeInterface; /** * Class Full reindex action + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction +class Full extends AbstractAction { /** - * @var \Magento\Framework\EntityManager\MetadataPool + * @var MetadataPool */ private $metadataPool; /** - * @var \Magento\Framework\Indexer\BatchProviderInterface + * @var BatchProviderInterface */ private $batchProvider; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator + * @var BatchSizeCalculator */ private $batchSizeCalculator; @@ -36,44 +51,54 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Eav\AbstractAction private $activeTableSwitcher; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ private $scopeConfig; /** - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory - * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool - * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator $batchSizeCalculator + * @var QueryGenerator|null + */ + private $batchQueryGenerator; + + /** + * @param DecimalFactory $eavDecimalFactory + * @param SourceFactory $eavSourceFactory + * @param MetadataPool|null $metadataPool + * @param BatchProviderInterface|null $batchProvider + * @param BatchSizeCalculator $batchSizeCalculator * @param ActiveTableSwitcher|null $activeTableSwitcher - * @param \Magento\Framework\App\Config\ScopeConfigInterface|null $scopeConfig + * @param ScopeConfigInterface|null $scopeConfig + * @param QueryGenerator|null $batchQueryGenerator */ public function __construct( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory $eavDecimalFactory, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory $eavSourceFactory, - \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, - \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator $batchSizeCalculator = null, + DecimalFactory $eavDecimalFactory, + SourceFactory $eavSourceFactory, + MetadataPool $metadataPool = null, + BatchProviderInterface $batchProvider = null, + BatchSizeCalculator $batchSizeCalculator = null, ActiveTableSwitcher $activeTableSwitcher = null, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig = null + ScopeConfigInterface $scopeConfig = null, + QueryGenerator $batchQueryGenerator = null ) { - $this->scopeConfig = $scopeConfig ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\App\Config\ScopeConfigInterface::class + $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get( + ScopeConfigInterface::class ); parent::__construct($eavDecimalFactory, $eavSourceFactory, $scopeConfig); - $this->metadataPool = $metadataPool ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\EntityManager\MetadataPool::class + $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get( + MetadataPool::class ); - $this->batchProvider = $batchProvider ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Framework\Indexer\BatchProviderInterface::class + $this->batchProvider = $batchProvider ?: ObjectManager::getInstance()->get( + BatchProviderInterface::class ); - $this->batchSizeCalculator = $batchSizeCalculator ?: \Magento\Framework\App\ObjectManager::getInstance()->get( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator::class + $this->batchSizeCalculator = $batchSizeCalculator ?: ObjectManager::getInstance()->get( + BatchSizeCalculator::class ); - $this->activeTableSwitcher = $activeTableSwitcher ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get( ActiveTableSwitcher::class ); + $this->batchQueryGenerator = $batchQueryGenerator ?: ObjectManager::getInstance()->get( + QueryGenerator::class + ); } /** @@ -81,10 +106,10 @@ public function __construct( * * @param array|int|null $ids * @return void - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function execute($ids = null) + public function execute($ids = null): void { if (!$this->isEavIndexerEnabled()) { return; @@ -94,20 +119,21 @@ public function execute($ids = null) $connection = $indexer->getConnection(); $mainTable = $this->activeTableSwitcher->getAdditionalTableName($indexer->getMainTable()); $connection->truncateTable($mainTable); - $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); - $batches = $this->batchProvider->getBatches( - $connection, - $entityMetadata->getEntityTable(), + $entityMetadata = $this->metadataPool->getMetadata(ProductInterface::class); + + $select = $connection->select(); + $select->distinct(true); + $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); + + $batchQueries = $this->batchQueryGenerator->generate( $entityMetadata->getIdentifierField(), - $this->batchSizeCalculator->estimateBatchSize($connection, $indexerName) + $select, + $this->batchSizeCalculator->estimateBatchSize($connection, $indexerName), + BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR ); - foreach ($batches as $batch) { - /** @var \Magento\Framework\DB\Select $select */ - $select = $connection->select(); - $select->distinct(true); - $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); - $entityIds = $this->batchProvider->getBatchIds($connection, $select, $batch); + foreach ($batchQueries as $query) { + $entityIds = $connection->fetchCol($query); if (!empty($entityIds)) { $indexer->reindexEntities($this->processRelations($indexer, $entityIds, true)); $this->syncData($indexer, $mainTable); @@ -116,14 +142,14 @@ public function execute($ids = null) $this->activeTableSwitcher->switchTable($indexer->getConnection(), [$indexer->getMainTable()]); } } catch (\Exception $e) { - throw new \Magento\Framework\Exception\LocalizedException(__($e->getMessage()), $e); + throw new LocalizedException(__($e->getMessage()), $e); } } /** * @inheritdoc */ - protected function syncData($indexer, $destinationTable, $ids = null) + protected function syncData($indexer, $destinationTable, $ids = null): void { $connection = $indexer->getConnection(); $connection->beginTransaction(); @@ -136,7 +162,7 @@ protected function syncData($indexer, $destinationTable, $ids = null) $select, $destinationTable, $targetColumns, - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + AdapterInterface::INSERT_ON_DUPLICATE ); $connection->query($query); $connection->commit(); @@ -155,7 +181,7 @@ private function isEavIndexerEnabled(): bool { $eavIndexerStatus = $this->scopeConfig->getValue( self::ENABLE_EAV_INDEXER, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE + ScopeInterface::SCOPE_STORE ); return (bool)$eavIndexerStatus; diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php index 1a75751570658..9b83051e6334b 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php @@ -3,41 +3,66 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Catalog\Model\Indexer\Product\Price\Action; +use Exception; +use Magento\Catalog\Model\Indexer\Product\Price\AbstractAction; +use Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory; +use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory; +use Magento\Directory\Model\CurrencyFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\PriceInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Query\BatchIterator; +use Magento\Framework\DB\Query\Generator as QueryGenerator; +use Magento\Framework\DB\Select; use Magento\Framework\EntityManager\EntityMetadataInterface; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Indexer\BatchProviderInterface; use Magento\Framework\Indexer\DimensionalIndexerInterface; use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; +use Magento\Framework\Stdlib\DateTime; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Indexer\Model\ProcessManager; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; +use Magento\Store\Model\StoreManagerInterface; +use SplFixedArray; /** * Class Full reindex action * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Full extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction +class Full extends AbstractAction { /** - * @var \Magento\Framework\EntityManager\MetadataPool + * @var MetadataPool */ private $metadataPool; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator + * @var BatchSizeCalculator */ private $batchSizeCalculator; /** - * @var \Magento\Framework\Indexer\BatchProviderInterface + * @var BatchProviderInterface */ private $batchProvider; /** - * @var \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher + * @var ActiveTableSwitcher */ private $activeTableSwitcher; @@ -47,54 +72,61 @@ class Full extends \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction private $productMetaDataCached; /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory + * @var DimensionCollectionFactory */ private $dimensionCollectionFactory; /** - * @var \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer + * @var TableMaintainer */ private $dimensionTableMaintainer; /** - * @var \Magento\Indexer\Model\ProcessManager + * @var ProcessManager */ private $processManager; /** - * @param \Magento\Framework\App\Config\ScopeConfigInterface $config - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory - * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate - * @param \Magento\Framework\Stdlib\DateTime $dateTime - * @param \Magento\Catalog\Model\Product\Type $catalogProductType - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory $indexerPriceFactory - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $defaultIndexerResource - * @param \Magento\Framework\EntityManager\MetadataPool|null $metadataPool - * @param \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator|null $batchSizeCalculator - * @param \Magento\Framework\Indexer\BatchProviderInterface|null $batchProvider - * @param \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher|null $activeTableSwitcher - * @param \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory|null $dimensionCollectionFactory - * @param \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer|null $dimensionTableMaintainer - * @param \Magento\Indexer\Model\ProcessManager $processManager + * @var QueryGenerator|null + */ + private $batchQueryGenerator; + + /** + * @param ScopeConfigInterface $config + * @param StoreManagerInterface $storeManager + * @param CurrencyFactory $currencyFactory + * @param TimezoneInterface $localeDate + * @param DateTime $dateTime + * @param Type $catalogProductType + * @param Factory $indexerPriceFactory + * @param DefaultPrice $defaultIndexerResource + * @param MetadataPool|null $metadataPool + * @param BatchSizeCalculator|null $batchSizeCalculator + * @param BatchProviderInterface|null $batchProvider + * @param ActiveTableSwitcher|null $activeTableSwitcher + * @param DimensionCollectionFactory|null $dimensionCollectionFactory + * @param TableMaintainer|null $dimensionTableMaintainer + * @param ProcessManager $processManager + * @param QueryGenerator|null $batchQueryGenerator * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $config, - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Directory\Model\CurrencyFactory $currencyFactory, - \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, - \Magento\Framework\Stdlib\DateTime $dateTime, - \Magento\Catalog\Model\Product\Type $catalogProductType, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Factory $indexerPriceFactory, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $defaultIndexerResource, - \Magento\Framework\EntityManager\MetadataPool $metadataPool = null, - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator $batchSizeCalculator = null, - \Magento\Framework\Indexer\BatchProviderInterface $batchProvider = null, - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher $activeTableSwitcher = null, - \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory $dimensionCollectionFactory = null, - \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer $dimensionTableMaintainer = null, - \Magento\Indexer\Model\ProcessManager $processManager = null + ScopeConfigInterface $config, + StoreManagerInterface $storeManager, + CurrencyFactory $currencyFactory, + TimezoneInterface $localeDate, + DateTime $dateTime, + Type $catalogProductType, + Factory $indexerPriceFactory, + DefaultPrice $defaultIndexerResource, + MetadataPool $metadataPool = null, + BatchSizeCalculator $batchSizeCalculator = null, + BatchProviderInterface $batchProvider = null, + ActiveTableSwitcher $activeTableSwitcher = null, + DimensionCollectionFactory $dimensionCollectionFactory = null, + TableMaintainer $dimensionTableMaintainer = null, + ProcessManager $processManager = null, + QueryGenerator $batchQueryGenerator = null ) { parent::__construct( $config, @@ -107,26 +139,27 @@ public function __construct( $defaultIndexerResource ); $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get( - \Magento\Framework\EntityManager\MetadataPool::class + MetadataPool::class ); $this->batchSizeCalculator = $batchSizeCalculator ?: ObjectManager::getInstance()->get( - \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BatchSizeCalculator::class + BatchSizeCalculator::class ); $this->batchProvider = $batchProvider ?: ObjectManager::getInstance()->get( - \Magento\Framework\Indexer\BatchProviderInterface::class + BatchProviderInterface::class ); $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance()->get( - \Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher::class + ActiveTableSwitcher::class ); $this->dimensionCollectionFactory = $dimensionCollectionFactory ?: ObjectManager::getInstance()->get( - \Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory::class + DimensionCollectionFactory::class ); $this->dimensionTableMaintainer = $dimensionTableMaintainer ?: ObjectManager::getInstance()->get( - \Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer::class + TableMaintainer::class ); $this->processManager = $processManager ?: ObjectManager::getInstance()->get( - \Magento\Indexer\Model\ProcessManager::class + ProcessManager::class ); + $this->batchQueryGenerator = $batchQueryGenerator ?? ObjectManager::getInstance()->get(QueryGenerator::class); } /** @@ -134,16 +167,16 @@ public function __construct( * * @param array|int|null $ids * @return void - * @throws \Exception + * @throws Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function execute($ids = null) + public function execute($ids = null): void { try { //Prepare indexer tables before full reindex $this->prepareTables(); - /** @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\DefaultPrice $indexer */ + /** @var DefaultPrice $indexer */ foreach ($this->getTypeIndexers(true) as $typeId => $priceIndexer) { if ($priceIndexer instanceof DimensionalIndexerInterface) { //New price reindex mechanism @@ -159,7 +192,7 @@ public function execute($ids = null) //Final replacement of tables from replica to main $this->switchTables(); - } catch (\Exception $e) { + } catch (Exception $e) { throw new LocalizedException(__($e->getMessage()), $e); } } @@ -168,9 +201,9 @@ public function execute($ids = null) * Prepare indexer tables before full reindex * * @return void - * @throws \Exception + * @throws Exception */ - private function prepareTables() + private function prepareTables(): void { $this->_defaultIndexerResource->getTableStrategy()->setUseIdxTable(false); @@ -183,9 +216,9 @@ private function prepareTables() * Truncate replica tables by dimensions * * @return void - * @throws \Exception + * @throws Exception */ - private function truncateReplicaTables() + private function truncateReplicaTables(): void { foreach ($this->dimensionCollectionFactory->create() as $dimension) { $dimensionTable = $this->dimensionTableMaintainer->getMainReplicaTable($dimension); @@ -200,14 +233,14 @@ private function truncateReplicaTables() * @param string $typeId * * @return void - * @throws \Exception + * @throws Exception */ - private function reindexProductTypeWithDimensions(DimensionalIndexerInterface $priceIndexer, string $typeId) + private function reindexProductTypeWithDimensions(DimensionalIndexerInterface $priceIndexer, string $typeId): void { $userFunctions = []; foreach ($this->dimensionCollectionFactory->create() as $dimensions) { $userFunctions[] = function () use ($priceIndexer, $dimensions, $typeId) { - return $this->reindexByBatches($priceIndexer, $dimensions, $typeId); + $this->reindexByBatches($priceIndexer, $dimensions, $typeId); }; } $this->processManager->execute($userFunctions); @@ -221,12 +254,15 @@ private function reindexProductTypeWithDimensions(DimensionalIndexerInterface $p * @param string $typeId * * @return void - * @throws \Exception + * @throws Exception */ - private function reindexByBatches(DimensionalIndexerInterface $priceIndexer, array $dimensions, string $typeId) - { + private function reindexByBatches( + DimensionalIndexerInterface $priceIndexer, + array $dimensions, + string $typeId + ): void { foreach ($this->getBatchesForIndexer($typeId) as $batch) { - $this->reindexByBatchWithDimensions($priceIndexer, $batch, $dimensions, $typeId); + $this->reindexByBatchWithDimensions($priceIndexer, $batch, $dimensions); } } @@ -235,16 +271,20 @@ private function reindexByBatches(DimensionalIndexerInterface $priceIndexer, arr * * @param string $typeId * - * @return \Generator - * @throws \Exception + * @return BatchIterator + * @throws Exception */ - private function getBatchesForIndexer(string $typeId) + private function getBatchesForIndexer(string $typeId): BatchIterator { $connection = $this->_defaultIndexerResource->getConnection(); - return $this->batchProvider->getBatches( - $connection, - $this->getProductMetaData()->getEntityTable(), + $entityMetadata = $this->getProductMetaData(); + $select = $connection->select(); + $select->distinct(true); + $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); + + return $this->batchQueryGenerator->generate( $this->getProductMetaData()->getIdentifierField(), + $select, $this->batchSizeCalculator->estimateBatchSize( $connection, $typeId @@ -256,27 +296,25 @@ private function getBatchesForIndexer(string $typeId) * Reindex by batch for new 'Dimensional' price indexer * * @param DimensionalIndexerInterface $priceIndexer - * @param array $batch + * @param Select $batchQuery * @param array $dimensions - * @param string $typeId * * @return void - * @throws \Exception + * @throws Exception */ private function reindexByBatchWithDimensions( DimensionalIndexerInterface $priceIndexer, - array $batch, - array $dimensions, - string $typeId - ) { - $entityIds = $this->getEntityIdsFromBatch($typeId, $batch); + Select $batchQuery, + array $dimensions + ): void { + $entityIds = $this->getEntityIdsFromBatch($batchQuery); if (!empty($entityIds)) { $this->dimensionTableMaintainer->createMainTmpTable($dimensions); $temporaryTable = $this->dimensionTableMaintainer->getMainTmpTable($dimensions); $this->_emptyTable($temporaryTable); - $priceIndexer->executeByDimensions($dimensions, \SplFixedArray::fromArray($entityIds, false)); + $priceIndexer->executeByDimensions($dimensions, SplFixedArray::fromArray($entityIds, false)); // Sync data from temp table to index table $this->_insertFromTable( @@ -293,12 +331,12 @@ private function reindexByBatchWithDimensions( * @param string $typeId * * @return void - * @throws \Exception + * @throws Exception */ - private function reindexProductType(PriceInterface $priceIndexer, string $typeId) + private function reindexProductType(PriceInterface $priceIndexer, string $typeId): void { foreach ($this->getBatchesForIndexer($typeId) as $batch) { - $this->reindexBatch($priceIndexer, $batch, $typeId); + $this->reindexBatch($priceIndexer, $batch); } } @@ -306,15 +344,13 @@ private function reindexProductType(PriceInterface $priceIndexer, string $typeId * Reindex by batch for old price indexer * * @param PriceInterface $priceIndexer - * @param array $batch - * @param string $typeId - * + * @param Select $batch * @return void - * @throws \Exception + * @throws Exception */ - private function reindexBatch(PriceInterface $priceIndexer, array $batch, string $typeId) + private function reindexBatch(PriceInterface $priceIndexer, Select $batch): void { - $entityIds = $this->getEntityIdsFromBatch($typeId, $batch); + $entityIds = $this->getEntityIdsFromBatch($batch); if (!empty($entityIds)) { // Temporary table will created if not exists @@ -339,36 +375,24 @@ private function reindexBatch(PriceInterface $priceIndexer, array $batch, string /** * Get Entity Ids from batch * - * @param string $typeId - * @param array $batch - * + * @param Select $batch * @return array - * @throws \Exception + * @throws Exception */ - private function getEntityIdsFromBatch(string $typeId, array $batch) + private function getEntityIdsFromBatch(Select $batch): array { $connection = $this->_defaultIndexerResource->getConnection(); - // Get entity ids from batch - $select = $connection - ->select() - ->distinct(true) - ->from( - ['e' => $this->getProductMetaData()->getEntityTable()], - $this->getProductMetaData()->getIdentifierField() - ) - ->where('type_id = ?', $typeId); - - return $this->batchProvider->getBatchIds($connection, $select, $batch); + return $connection->fetchCol($batch); } /** * Get product meta data * * @return EntityMetadataInterface - * @throws \Exception + * @throws Exception */ - private function getProductMetaData() + private function getProductMetaData(): EntityMetadataInterface { if ($this->productMetaDataCached === null) { $this->productMetaDataCached = $this->metadataPool->getMetadata(ProductInterface::class); @@ -381,9 +405,9 @@ private function getProductMetaData() * Get replica table * * @return string - * @throws \Exception + * @throws Exception */ - private function getReplicaTable() + private function getReplicaTable(): string { return $this->activeTableSwitcher->getAdditionalTableName( $this->_defaultIndexerResource->getMainTable() @@ -395,7 +419,7 @@ private function getReplicaTable() * * @return void */ - private function switchTables() + private function switchTables(): void { // Switch dimension tables $mainTablesByDimension = []; @@ -417,13 +441,13 @@ private function switchTables() /** * Move data from old price indexer mechanism to new indexer mechanism by dimensions. + * * Used only for backward compatibility * * @param array $dimensions - * * @return void */ - private function moveDataFromReplicaTableToReplicaTables(array $dimensions) + private function moveDataFromReplicaTableToReplicaTables(array $dimensions): void { if (!$dimensions) { return; @@ -455,17 +479,17 @@ private function moveDataFromReplicaTableToReplicaTables(array $dimensions) $select, $replicaTablesByDimension, [], - \Magento\Framework\DB\Adapter\AdapterInterface::INSERT_ON_DUPLICATE + AdapterInterface::INSERT_ON_DUPLICATE ) ); } /** - * @deprecated + * Retrieves the index table that should be used * - * @inheritdoc + * @deprecated */ - protected function getIndexTargetTable() + protected function getIndexTargetTable(): string { return $this->activeTableSwitcher->getAdditionalTableName($this->_defaultIndexerResource->getMainTable()); } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php index 90c3f999a6a8b..2e1cff834fd34 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Eav/Action/FullTest.php @@ -3,15 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +declare(strict_types=1); + namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Eav\Action; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Indexer\Product\Eav\Action\Full; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Decimal; +use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Query\Generator; +use Magento\Framework\DB\Select; +use Magento\Framework\EntityManager\EntityMetadataInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\DecimalFactory; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\SourceFactory; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Indexer\BatchProviderInterface; use Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\BatchSizeCalculator; +use PHPUnit\Framework\MockObject\MockObject as MockObject; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -19,45 +33,50 @@ class FullTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full|\PHPUnit_Framework_MockObject_MockObject + * @var Full|MockObject */ private $model; /** - * @var DecimalFactory|\PHPUnit_Framework_MockObject_MockObject + * @var DecimalFactory|MockObject */ private $eavDecimalFactory; /** - * @var SourceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var SourceFactory|MockObject */ private $eavSourceFactory; /** - * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject + * @var MetadataPool|MockObject */ private $metadataPool; /** - * @var BatchProviderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var BatchProviderInterface|MockObject */ private $batchProvider; /** - * @var BatchSizeCalculator|\PHPUnit_Framework_MockObject_MockObject + * @var BatchSizeCalculator|MockObject */ private $batchSizeCalculator; /** - * @var ActiveTableSwitcher|\PHPUnit_Framework_MockObject_MockObject + * @var ActiveTableSwitcher|MockObject */ private $activeTableSwitcher; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ScopeConfigInterface|MockObject */ private $scopeConfig; + /** + * @var Generator + */ + private $batchQueryGenerator; + /** * @return void */ @@ -67,15 +86,16 @@ protected function setUp() $this->eavSourceFactory = $this->createPartialMock(SourceFactory::class, ['create']); $this->metadataPool = $this->createMock(MetadataPool::class); $this->batchProvider = $this->getMockForAbstractClass(BatchProviderInterface::class); + $this->batchQueryGenerator = $this->createMock(Generator::class); $this->batchSizeCalculator = $this->createMock(BatchSizeCalculator::class); $this->activeTableSwitcher = $this->createMock(ActiveTableSwitcher::class); - $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); $objectManager = new ObjectManager($this); $this->model = $objectManager->getObject( - \Magento\Catalog\Model\Indexer\Product\Eav\Action\Full::class, + Full::class, [ 'eavDecimalFactory' => $this->eavDecimalFactory, 'eavSourceFactory' => $this->eavSourceFactory, @@ -83,7 +103,8 @@ protected function setUp() 'batchProvider' => $this->batchProvider, 'batchSizeCalculator' => $this->batchSizeCalculator, 'activeTableSwitcher' => $this->activeTableSwitcher, - 'scopeConfig' => $this->scopeConfig + 'scopeConfig' => $this->scopeConfig, + 'batchQueryGenerator' => $this->batchQueryGenerator, ] ); } @@ -96,15 +117,15 @@ public function testExecute() $this->scopeConfig->expects($this->once())->method('getValue')->willReturn(1); $ids = [1, 2, 3]; - $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + $connectionMock = $this->getMockBuilder(AdapterInterface::class) ->getMockForAbstractClass(); $connectionMock->expects($this->atLeastOnce())->method('describeTable')->willReturn(['id' => []]); - $eavSource = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Source::class) + $eavSource = $this->getMockBuilder(Source::class) ->disableOriginalConstructor() ->getMock(); - $eavDecimal = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\Decimal::class) + $eavDecimal = $this->getMockBuilder(Decimal::class) ->disableOriginalConstructor() ->getMock(); @@ -125,22 +146,28 @@ public function testExecute() $this->eavSourceFactory->expects($this->once())->method('create')->will($this->returnValue($eavDecimal)); - $entityMetadataMock = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) + $entityMetadataMock = $this->getMockBuilder(EntityMetadataInterface::class) ->getMockForAbstractClass(); $this->metadataPool->expects($this->atLeastOnce()) ->method('getMetadata') - ->with(\Magento\Catalog\Api\Data\ProductInterface::class) + ->with(ProductInterface::class) ->willReturn($entityMetadataMock); - $this->batchProvider->expects($this->atLeastOnce()) - ->method('getBatches') - ->willReturn([['from' => 10, 'to' => 100]]); - $this->batchProvider->expects($this->atLeastOnce()) - ->method('getBatchIds') + // Super inefficient algorithm in some cases + $this->batchProvider->expects($this->never()) + ->method('getBatches'); + + $batchQuery = $this->createMock(Select::class); + + $connectionMock->method('fetchCol') + ->with($batchQuery) ->willReturn($ids); - $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + $this->batchQueryGenerator->method('generate') + ->willReturn([$batchQuery]); + + $selectMock = $this->getMockBuilder(Select::class) ->disableOriginalConstructor() ->getMock(); @@ -153,7 +180,7 @@ public function testExecute() /** * @return void - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function testExecuteWithDisabledEavIndexer() { diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php index bc10d38173b4d..43a5aabee9779 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/Action/Full.php @@ -6,12 +6,19 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogInventory\Model\Indexer\Stock\Action; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher; +use Magento\CatalogInventory\Model\Indexer\Stock\BatchSizeManagement; +use Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock; use Magento\Framework\App\ResourceConnection; use Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory; use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Framework\DB\Query\BatchIteratorInterface; +use Magento\Framework\DB\Query\Generator as QueryGenerator; use Magento\Framework\Indexer\CacheContext; use Magento\Framework\Event\ManagerInterface as EventManager; use Magento\Framework\EntityManager\MetadataPool; @@ -25,7 +32,6 @@ /** * Class Full reindex action * - * @package Magento\CatalogInventory\Model\Indexer\Stock\Action * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Full extends AbstractAction @@ -60,6 +66,11 @@ class Full extends AbstractAction */ private $activeTableSwitcher; + /** + * @var QueryGenerator|null + */ + private $batchQueryGenerator; + /** * @param ResourceConnection $resource * @param StockFactory $indexerFactory @@ -71,7 +82,7 @@ class Full extends AbstractAction * @param BatchProviderInterface|null $batchProvider * @param array $batchRowsCount * @param ActiveTableSwitcher|null $activeTableSwitcher - * + * @param QueryGenerator|null $batchQueryGenerator * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -84,7 +95,8 @@ public function __construct( BatchSizeManagementInterface $batchSizeManagement = null, BatchProviderInterface $batchProvider = null, array $batchRowsCount = [], - ActiveTableSwitcher $activeTableSwitcher = null + ActiveTableSwitcher $activeTableSwitcher = null, + QueryGenerator $batchQueryGenerator = null ) { parent::__construct( $resource, @@ -97,11 +109,12 @@ public function __construct( $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class); $this->batchProvider = $batchProvider ?: ObjectManager::getInstance()->get(BatchProviderInterface::class); $this->batchSizeManagement = $batchSizeManagement ?: ObjectManager::getInstance()->get( - \Magento\CatalogInventory\Model\Indexer\Stock\BatchSizeManagement::class + BatchSizeManagement::class ); $this->batchRowsCount = $batchRowsCount; $this->activeTableSwitcher = $activeTableSwitcher ?: ObjectManager::getInstance() ->get(ActiveTableSwitcher::class); + $this->batchQueryGenerator = $batchQueryGenerator ?: ObjectManager::getInstance()->get(QueryGenerator::class); } /** @@ -109,22 +122,20 @@ public function __construct( * * @param null|array $ids * @throws LocalizedException - * * @return void - * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function execute($ids = null) + public function execute($ids = null): void { try { $this->useIdxTable(false); $this->cleanIndexersTables($this->_getTypeIndexers()); - $entityMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + $entityMetadata = $this->metadataPool->getMetadata(ProductInterface::class); $columns = array_keys($this->_getConnection()->describeTable($this->_getIdxTable())); - /** @var \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock $indexer */ + /** @var DefaultStock $indexer */ foreach ($this->_getTypeIndexers() as $indexer) { $indexer->setActionType(self::ACTION_TYPE); $connection = $indexer->getConnection(); @@ -135,22 +146,21 @@ public function execute($ids = null) : $this->batchRowsCount['default']; $this->batchSizeManagement->ensureBatchSize($connection, $batchRowCount); - $batches = $this->batchProvider->getBatches( - $connection, - $entityMetadata->getEntityTable(), + + $select = $connection->select(); + $select->distinct(true); + $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); + + $batchQueries = $this->batchQueryGenerator->generate( $entityMetadata->getIdentifierField(), - $batchRowCount + $select, + $batchRowCount, + BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR ); - foreach ($batches as $batch) { + foreach ($batchQueries as $query) { $this->clearTemporaryIndexTable(); - // Get entity ids from batch - $select = $connection->select(); - $select->distinct(true); - $select->from(['e' => $entityMetadata->getEntityTable()], $entityMetadata->getIdentifierField()); - $select->where('type_id = ?', $indexer->getTypeId()); - - $entityIds = $this->batchProvider->getBatchIds($connection, $select, $batch); + $entityIds = $connection->fetchCol($query); if (!empty($entityIds)) { $indexer->reindexEntity($entityIds); $select = $connection->select()->from($this->_getIdxTable(), $columns); @@ -167,12 +177,13 @@ public function execute($ids = null) /** * Delete all records from index table + * * Used to clean table before re-indexation * * @param array $indexers * @return void */ - private function cleanIndexersTables(array $indexers) + private function cleanIndexersTables(array $indexers): void { $tables = array_map( function (StockInterface $indexer) { From 84507a3d5c0eb1b79c502626a0e4a18d80204279 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Thu, 21 Feb 2019 15:52:30 -0600 Subject: [PATCH 171/254] MC-4905: Convert CreateCustomUrlRewriteEntityTest to MFTF --- .../Section/AdminUrlRewriteEditSection.xml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml new file mode 100644 index 0000000000000..ba048752fcb5f --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.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="AdminUrlRewriteEditSection"> + <element name="createCustomUrlRewrite" type="select" selector="//select[@id='entity-type-selector']" /> + <element name="createCustomUrlRewriteValue" type="text" selector="//select[@id='entity-type-selector']/option[contains(.,'{{var}}')]" parameterized="true"/> + <element name="store" type="select" selector="//select[@id='store_id']"/> + <element name="storeValue" type="select" selector="//select[@id='store_id']//option[contains(., '{{var}}')]" parameterized="true" /> + <element name="requestPath" type="input" selector="//input[@id='request_path']"/> + <element name="targetPath" type="input" selector="//input[@id='target_path']"/> + <element name="redirectType" type="select" selector="//select[@id='redirect_type']"/> + <element name="redirectTypeValue" type="select" selector="//select[@id='redirect_type']//option[contains(., '{{Var}}')]" parameterized="true"/> + <element name="description" type="input" selector="#description"/> + <element name="categoryInTree" type="text" selector="//li[contains(@class,'active-category jstree-open')]/a[contains(., '{{var}}')]" parameterized="true"/> + <element name="saveButton" type="button" selector="#save" timeout="30"/> + <element name="deleteButton" type="button" selector="#delete" timeout="30"/> + <element name="okButton" type="button" selector="//button[@class='action-primary action-accept']" timeout="30"/> + </section> +</sections> \ No newline at end of file From 1d1bb3398afde0a426debe0e9a01df7aa29a3d2b Mon Sep 17 00:00:00 2001 From: Serhii Dzhepa <sdzhepa@adobe.com> Date: Thu, 21 Feb 2019 16:32:30 -0600 Subject: [PATCH 172/254] Fixed static tests fails --- .../web/css/source/module/checkout/_payments.less | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 7428b1a108b25..eb9c069053661 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 @@ -63,10 +63,13 @@ } } } - + /** + * @codingStandardsIgnoreStart + */ #po_number { margin-bottom: 20px; } + // @codingStandardsIgnoreEnd } .payment-method-title { @@ -116,9 +119,9 @@ margin: 0 0 @indent__base; .primary { - .action-update { - margin-right: 0; + .action-update { margin-bottom: 20px; + margin-right: 0; } } From c9fff890e5da3e9e173fd2163f8accc7ef5338e6 Mon Sep 17 00:00:00 2001 From: Jitheesh <jitheeshvo@gmail.com> Date: Fri, 22 Feb 2019 10:01:45 +0530 Subject: [PATCH 173/254] As low as displays incorrect pricing on category page, tax appears to be added twice #21383 - excluded tax from min price --- .../Catalog/Pricing/Price/MinimalTierPriceCalculator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php b/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php index 387ef9416ef68..af81e67e86fce 100644 --- a/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php +++ b/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php @@ -58,6 +58,6 @@ public function getAmount(SaleableInterface $saleableItem) return $value === null ? null - : $this->calculator->getAmount($value, $saleableItem); + : $this->calculator->getAmount($value, $saleableItem, 'tax'); } } From dfc943c7ce3878cc8e36d58d0f2e268a828fa081 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Thu, 21 Feb 2019 22:59:10 -0600 Subject: [PATCH 174/254] Enhance stability of tests using SetTaxApplyOnSetting action group --- .../Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml index 593bf95392633..510624eb369e2 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml @@ -36,7 +36,7 @@ <scrollTo selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" x="0" y="-80" stepKey="goToCheckbox"/> <uncheckOption selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" stepKey="enableApplyTaxOnSetting"/> <selectOption selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOn}}" userInput="{{userInput}}" stepKey="setApplyTaxOn"/> - <scrollTo selector="{{SalesConfigSection.TaxClassesTab}}" stepKey="scrollToTop"/> + <scrollTo selector="{{SalesConfigSection.TaxClassesTab}}" x="0" y="-80" stepKey="scrollToTop"/> <click selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" stepKey="collapseCalcSettingsTab"/> <click selector="{{AdminConfigureTaxSection.save}}" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForConfigSaved"/> @@ -56,4 +56,4 @@ <waitForPageLoad stepKey="waitForConfigSaved"/> <see userInput="You saved the configuration." stepKey="seeSuccessMessage"/> </actionGroup> - </actionGroups> \ No newline at end of file + </actionGroups> From b62691daed674f7795918fac74e2f17c79a2500a Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 22 Feb 2019 10:53:34 +0200 Subject: [PATCH 175/254] Fix static tests. --- .../Controller/Adminhtml/Product/GridOnly.php | 8 ++++++-- lib/internal/Magento/Framework/DataObject.php | 13 +++++++------ .../Magento/Framework/Module/PackageInfo.php | 5 +++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/GridOnly.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/GridOnly.php index 2ddd16b00dfd0..51aaa8c178edd 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/GridOnly.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/GridOnly.php @@ -1,12 +1,16 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Controller\Adminhtml\Product; -class GridOnly extends \Magento\Catalog\Controller\Adminhtml\Product +use Magento\Framework\App\Action\HttpGetActionInterface; + +/** + * Get specified tab grid controller. + */ +class GridOnly extends \Magento\Catalog\Controller\Adminhtml\Product implements HttpGetActionInterface { /** * @var \Magento\Framework\Controller\Result\RawFactory diff --git a/lib/internal/Magento/Framework/DataObject.php b/lib/internal/Magento/Framework/DataObject.php index 7f056d38c8fd7..6ecbca133e22a 100644 --- a/lib/internal/Magento/Framework/DataObject.php +++ b/lib/internal/Magento/Framework/DataObject.php @@ -64,8 +64,8 @@ public function addData(array $arr) * * If $key is an array, it will overwrite all the data in the object. * - * @param string|array $key - * @param mixed $value + * @param string|array $key + * @param mixed $value * @return $this */ public function setData($key, $value = null) @@ -111,7 +111,7 @@ public function unsetData($key = null) * and retrieve corresponding member. If data is the string - it will be explode * by new line character and converted to array. * - * @param string $key + * @param string $key * @param string|int $index * @return mixed */ @@ -222,6 +222,7 @@ public function getDataUsingMethod($key, $args = null) /** * If $key is empty, checks whether there's any data in the object + * * Otherwise checks if the specified attribute is set. * * @param string $key @@ -272,8 +273,8 @@ public function convertToArray(array $keys = []) /** * Convert object data into XML string * - * @param array $keys array of keys that must be represented - * @param string $rootName root node name + * @param array $keys array of keys that must be represented + * @param string $rootName root node name * @param bool $addOpenTag flag that allow to add initial xml node * @param bool $addCdata flag that require wrap all values in CDATA * @return string @@ -436,7 +437,7 @@ protected function _underscore($name) * * Example: key1="value1" key2="value2" ... * - * @param array $keys array of accepted keys + * @param array $keys array of accepted keys * @param string $valueSeparator separator between key and value * @param string $fieldSeparator separator between key/value pairs * @param string $quote quoting sign diff --git a/lib/internal/Magento/Framework/Module/PackageInfo.php b/lib/internal/Magento/Framework/Module/PackageInfo.php index 848ad4495f39e..1eeb2bafc9623 100644 --- a/lib/internal/Magento/Framework/Module/PackageInfo.php +++ b/lib/internal/Magento/Framework/Module/PackageInfo.php @@ -8,8 +8,9 @@ use Magento\Framework\Component\ComponentRegistrar; /** - * Provide information of dependencies and conflicts in composer.json files, mapping of package name to module name, - * and mapping of module name to package version + * Provide information of dependencies and conflicts in composer.json files. + * + * Mapping of package name to module name, and mapping of module name to package version. */ class PackageInfo { From f82299b746f6f7846d98693d343f6633e430c5c9 Mon Sep 17 00:00:00 2001 From: Wojtek Naruniec <wojtek@mediotype.com> Date: Fri, 22 Feb 2019 10:17:42 +0100 Subject: [PATCH 176/254] Move element id to component parameters, fix JS coding style --- .../Checkout/view/frontend/templates/cart/form.phtml | 3 ++- .../view/frontend/web/js/action/update-shopping-cart.js | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml index 1005c11e44d95..84ab9b13d8f3a 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml @@ -14,7 +14,8 @@ method="post" id="form-validate" data-mage-init='{"Magento_Checkout/js/action/update-shopping-cart": - {"validationURL" : "/checkout/cart/updateItemQty"} + {"validationURL" : "/checkout/cart/updateItemQty", + "updateCartActionContainer": "#update_cart_action_container"} }' class="form form-cart"> <?= $block->getBlockHtml('formkey') ?> 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 9d79e34b10f17..7686dcad6e050 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 @@ -14,7 +14,8 @@ define([ $.widget('mage.updateShoppingCart', { options: { validationURL: '', - eventName: 'updateCartItemQty' + eventName: 'updateCartItemQty', + updateCartActionContainer: '' }, /** @inheritdoc */ @@ -31,11 +32,12 @@ define([ * @return {Boolean} */ onSubmit: function (event) { + var action = this.element.find(this.options.updateCartActionContainer).val(); + if (!this.options.validationURL) { return true; } - var action = this.element.find('#update_cart_action_container').val(); if (action === 'empty_cart') { return true; } From 9d3bc1877c4d48eae2e0ef7e7acc33b617474ecc Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 22 Feb 2019 11:55:31 +0200 Subject: [PATCH 177/254] Fix static tests. --- lib/internal/Magento/Framework/Code/Generator/Autoloader.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php index 1afa97729b158..35c138147e9d3 100644 --- a/lib/internal/Magento/Framework/Code/Generator/Autoloader.php +++ b/lib/internal/Magento/Framework/Code/Generator/Autoloader.php @@ -10,6 +10,9 @@ use Magento\Framework\Code\Generator; use Psr\Log\LoggerInterface; +/** + * Class loader and generator. + */ class Autoloader { /** @@ -55,6 +58,8 @@ public function load($className) } /** + * Log exception. + * * @param \Exception $exception */ private function tryToLogExceptionMessageIfNotDuplicate(\Exception $exception): void From e1b23a146009a5ed3cedf94cee2436b39a3f5bb9 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Fri, 22 Feb 2019 12:58:15 +0200 Subject: [PATCH 178/254] Fix static tests. --- app/code/Magento/Marketplace/Block/Partners.php | 2 ++ app/code/Magento/PageCache/Model/Cache/Server.php | 3 +++ app/code/Magento/PageCache/Model/Varnish/VclGenerator.php | 8 +++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Marketplace/Block/Partners.php b/app/code/Magento/Marketplace/Block/Partners.php index a3ed88f4fc3b4..30d6a2910f4de 100644 --- a/app/code/Magento/Marketplace/Block/Partners.php +++ b/app/code/Magento/Marketplace/Block/Partners.php @@ -7,6 +7,8 @@ namespace Magento\Marketplace\Block; /** + * Partners section block. + * * @api * @since 100.0.2 */ diff --git a/app/code/Magento/PageCache/Model/Cache/Server.php b/app/code/Magento/PageCache/Model/Cache/Server.php index 06118446c21bc..7f3a4af969d7e 100644 --- a/app/code/Magento/PageCache/Model/Cache/Server.php +++ b/app/code/Magento/PageCache/Model/Cache/Server.php @@ -12,6 +12,9 @@ use Zend\Uri\Uri; use Zend\Uri\UriFactory; +/** + * Cache server model. + */ class Server { /** diff --git a/app/code/Magento/PageCache/Model/Varnish/VclGenerator.php b/app/code/Magento/PageCache/Model/Varnish/VclGenerator.php index 5a7c3a2d2f6d1..a50fa090de2d8 100644 --- a/app/code/Magento/PageCache/Model/Varnish/VclGenerator.php +++ b/app/code/Magento/PageCache/Model/Varnish/VclGenerator.php @@ -9,6 +9,9 @@ use Magento\PageCache\Model\VclGeneratorInterface; use Magento\PageCache\Model\VclTemplateLocatorInterface; +/** + * Varnish vcl generator model. + */ class VclGenerator implements VclGeneratorInterface { /** @@ -143,7 +146,8 @@ private function getRegexForDesignExceptions() /** * Get IPs access list that can purge Varnish configuration for config file generation - * and transform it to appropriate view + * + * Tansform it to appropriate view * * acl purge{ * "127.0.0.1"; @@ -216,6 +220,8 @@ private function getSslOffloadedHeader() } /** + * Get design exceptions array. + * * @return array */ private function getDesignExceptions() From 15ef6d792be5367872266cac611d5f6b9aa7810f Mon Sep 17 00:00:00 2001 From: Yogesh Suhagiya <yksuhagiya@gmail.com> Date: Fri, 22 Feb 2019 17:02:19 +0530 Subject: [PATCH 179/254] Corrected return types --- app/code/Magento/Fedex/Model/Carrier.php | 1 - .../GiftMessage/Block/Adminhtml/Sales/Order/View/Items.php | 2 +- app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php | 2 +- app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Fedex/Model/Carrier.php b/app/code/Magento/Fedex/Model/Carrier.php index 955345851e67a..0cbd90150734b 100644 --- a/app/code/Magento/Fedex/Model/Carrier.php +++ b/app/code/Magento/Fedex/Model/Carrier.php @@ -363,7 +363,6 @@ public function setRequest(RateRequest $request) if ($request->getDestPostcode()) { $r->setDestPostal($request->getDestPostcode()); - } else { } if ($request->getDestCity()) { diff --git a/app/code/Magento/GiftMessage/Block/Adminhtml/Sales/Order/View/Items.php b/app/code/Magento/GiftMessage/Block/Adminhtml/Sales/Order/View/Items.php index c923ced8918d2..c15b76583187a 100644 --- a/app/code/Magento/GiftMessage/Block/Adminhtml/Sales/Order/View/Items.php +++ b/app/code/Magento/GiftMessage/Block/Adminhtml/Sales/Order/View/Items.php @@ -171,7 +171,7 @@ public function getMessage() /** * Retrieve save url * - * @return array + * @return string * @codeCoverageIgnore */ public function getSaveUrl() diff --git a/app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php b/app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php index 5da8b3b55700e..6483ec8be6335 100644 --- a/app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php +++ b/app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php @@ -76,7 +76,7 @@ public function getJsLayout() /** * Retrieve gift message configuration * - * @return array + * @return string */ public function getGiftOptionsConfigJson() { diff --git a/app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php b/app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php index adb500a818517..d09f882820a92 100644 --- a/app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php +++ b/app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php @@ -32,7 +32,7 @@ public function __construct( /** * @param \Magento\Checkout\Model\Type\Onepage $subject * @param array $result - * @return $this + * @return array */ public function afterSaveShippingMethod( \Magento\Checkout\Model\Type\Onepage $subject, From a1578b550ef678f9821a6f6756e3efe5206162be Mon Sep 17 00:00:00 2001 From: Oleksandr Kravchuk <swnsma@gmail.com> Date: Fri, 22 Feb 2019 13:46:04 +0200 Subject: [PATCH 180/254] Simplify. --- .../view/frontend/web/js/action/update-shopping-cart.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 7686dcad6e050..1920bc4d7ac41 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 @@ -34,11 +34,7 @@ define([ onSubmit: function (event) { var action = this.element.find(this.options.updateCartActionContainer).val(); - if (!this.options.validationURL) { - return true; - } - - if (action === 'empty_cart') { + if (!this.options.validationURL || action === 'empty_cart') { return true; } From 4350a2e5a7ba4ddda00d50c6f55906a3d9f44e25 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Fri, 22 Feb 2019 07:52:16 -0600 Subject: [PATCH 181/254] MC-15004: Indexer hangs on category/products index - CR feedback --- .../Indexer/Category/Product/Action/Full.php | 2 +- .../Indexer/Product/Price/Action/Full.php | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php index e7b1e2c9bd62f..eb59acb56c356 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php @@ -110,7 +110,7 @@ public function __construct( } /** - * Creates the store tables + * Create the store tables * * @return void */ diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php index 9b83051e6334b..858eba3ab217a 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/Action/Full.php @@ -8,7 +8,6 @@ namespace Magento\Catalog\Model\Indexer\Product\Price\Action; -use Exception; use Magento\Catalog\Model\Indexer\Product\Price\AbstractAction; use Magento\Catalog\Model\Indexer\Product\Price\DimensionCollectionFactory; use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer; @@ -37,7 +36,6 @@ use Magento\Indexer\Model\ProcessManager; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Store\Model\StoreManagerInterface; -use SplFixedArray; /** * Class Full reindex action @@ -167,7 +165,7 @@ public function __construct( * * @param array|int|null $ids * @return void - * @throws Exception + * @throws \Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute($ids = null): void @@ -192,7 +190,7 @@ public function execute($ids = null): void //Final replacement of tables from replica to main $this->switchTables(); - } catch (Exception $e) { + } catch (\Exception $e) { throw new LocalizedException(__($e->getMessage()), $e); } } @@ -201,7 +199,7 @@ public function execute($ids = null): void * Prepare indexer tables before full reindex * * @return void - * @throws Exception + * @throws \Exception */ private function prepareTables(): void { @@ -216,7 +214,7 @@ private function prepareTables(): void * Truncate replica tables by dimensions * * @return void - * @throws Exception + * @throws \Exception */ private function truncateReplicaTables(): void { @@ -233,7 +231,7 @@ private function truncateReplicaTables(): void * @param string $typeId * * @return void - * @throws Exception + * @throws \Exception */ private function reindexProductTypeWithDimensions(DimensionalIndexerInterface $priceIndexer, string $typeId): void { @@ -254,7 +252,7 @@ private function reindexProductTypeWithDimensions(DimensionalIndexerInterface $p * @param string $typeId * * @return void - * @throws Exception + * @throws \Exception */ private function reindexByBatches( DimensionalIndexerInterface $priceIndexer, @@ -272,7 +270,7 @@ private function reindexByBatches( * @param string $typeId * * @return BatchIterator - * @throws Exception + * @throws \Exception */ private function getBatchesForIndexer(string $typeId): BatchIterator { @@ -300,7 +298,7 @@ private function getBatchesForIndexer(string $typeId): BatchIterator * @param array $dimensions * * @return void - * @throws Exception + * @throws \Exception */ private function reindexByBatchWithDimensions( DimensionalIndexerInterface $priceIndexer, @@ -314,7 +312,7 @@ private function reindexByBatchWithDimensions( $temporaryTable = $this->dimensionTableMaintainer->getMainTmpTable($dimensions); $this->_emptyTable($temporaryTable); - $priceIndexer->executeByDimensions($dimensions, SplFixedArray::fromArray($entityIds, false)); + $priceIndexer->executeByDimensions($dimensions, \SplFixedArray::fromArray($entityIds, false)); // Sync data from temp table to index table $this->_insertFromTable( @@ -331,7 +329,7 @@ private function reindexByBatchWithDimensions( * @param string $typeId * * @return void - * @throws Exception + * @throws \Exception */ private function reindexProductType(PriceInterface $priceIndexer, string $typeId): void { @@ -346,7 +344,7 @@ private function reindexProductType(PriceInterface $priceIndexer, string $typeId * @param PriceInterface $priceIndexer * @param Select $batch * @return void - * @throws Exception + * @throws \Exception */ private function reindexBatch(PriceInterface $priceIndexer, Select $batch): void { @@ -377,7 +375,7 @@ private function reindexBatch(PriceInterface $priceIndexer, Select $batch): void * * @param Select $batch * @return array - * @throws Exception + * @throws \Exception */ private function getEntityIdsFromBatch(Select $batch): array { @@ -390,7 +388,7 @@ private function getEntityIdsFromBatch(Select $batch): array * Get product meta data * * @return EntityMetadataInterface - * @throws Exception + * @throws \Exception */ private function getProductMetaData(): EntityMetadataInterface { @@ -405,7 +403,7 @@ private function getProductMetaData(): EntityMetadataInterface * Get replica table * * @return string - * @throws Exception + * @throws \Exception */ private function getReplicaTable(): string { @@ -418,6 +416,7 @@ private function getReplicaTable(): string * Replacement of tables from replica to main * * @return void + * @throws \Zend_Db_Statement_Exception */ private function switchTables(): void { @@ -446,6 +445,7 @@ private function switchTables(): void * * @param array $dimensions * @return void + * @throws \Zend_Db_Statement_Exception */ private function moveDataFromReplicaTableToReplicaTables(array $dimensions): void { From 5890b105bed6e83cf844d690be88f5b1da123ec7 Mon Sep 17 00:00:00 2001 From: Kavitha <kanair@adobe.com> Date: Fri, 22 Feb 2019 09:55:21 -0600 Subject: [PATCH 182/254] MC-4905: Convert CreateCustomUrlRewriteEntityTest to MFTF --- .../ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml | 4 ++-- .../Test/Mftf/Section/AdminUrlRewriteEditSection.xml | 2 +- .../Test/Mftf/Section/AdminUrlRewriteIndexSection.xml | 2 +- ...ateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml | 4 ++-- ...eateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml index cdbd3d146a5b2..7223c9bdb3205 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/StorefrontUrlRewriteRedirectActionGroup.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="StorefrontUrlRewriteRedirect"> + <actionGroup name="AssertStorefrontUrlRewriteRedirect"> <arguments> <argument name="category" type="string"/> <argument name="newRequestPath" type="string"/> @@ -18,7 +18,7 @@ <seeElement selector="{{StorefrontHeaderSection.NavigationCategoryByName(category)}}" stepKey="seeCategoryOnStoreNavigationBar"/> <seeElement selector="{{StorefrontCategoryMainSection.CategoryTitle(category)}}" stepKey="seeCategoryInTitle"/> </actionGroup> - <actionGroup name="StorefrontProductRedirect"> + <actionGroup name="AssertStorefrontProductRedirect"> <arguments> <argument name="productName" type="string"/> <argument name="productSku" type="string"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml index ba048752fcb5f..2bdc6872088ec 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteEditSection.xml @@ -18,7 +18,7 @@ <element name="redirectType" type="select" selector="//select[@id='redirect_type']"/> <element name="redirectTypeValue" type="select" selector="//select[@id='redirect_type']//option[contains(., '{{Var}}')]" parameterized="true"/> <element name="description" type="input" selector="#description"/> - <element name="categoryInTree" type="text" selector="//li[contains(@class,'active-category jstree-open')]/a[contains(., '{{var}}')]" parameterized="true"/> + <element name="categoryInTree" type="text" selector="//li[contains(@class,'active-category jstree-open')]/a[contains(., '{{categoryName}}')]" parameterized="true"/> <element name="saveButton" type="button" selector="#save" timeout="30"/> <element name="deleteButton" type="button" selector="#delete" timeout="30"/> <element name="okButton" type="button" selector="//button[@class='action-primary action-accept']" timeout="30"/> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml index ef86b2eb7223a..90ab19c2ed76a 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Section/AdminUrlRewriteIndexSection.xml @@ -20,6 +20,6 @@ <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'][{{var1}}]/td/a[contains(.,'Edit')]" parameterized="true"/> + <element name="editButton" type="text" selector="//tr[@data-role='row'][{{rowNumber}}]/td/a[contains(.,'Edit')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml index 64bb6c5fa13c0..b123bc14cb1ed 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomCategoryUrlRewriteAndAddPermanentRedirectTest.xml @@ -60,13 +60,13 @@ </actionGroup> <!-- Assert updated Category redirect in Store Front --> - <actionGroup ref="StorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront"> <argument name="category" value="$$category.name$$"/> <argument name="newRequestPath" value="{{FirstLevelSubCat.name}}.html"/> </actionGroup> <!-- Assert initial Category redirect in Store Front --> - <actionGroup ref="StorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront1"> + <actionGroup ref="AssertStorefrontUrlRewriteRedirect" stepKey="verifyCategoryInStoreFront1"> <argument name="category" value="$$category.name$$"/> <argument name="newRequestPath" value="catalog/category/view/id/{$categoryId}"/> </actionGroup> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml index 2086f16cd4f7b..711d5389b013b 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateCustomProductUrlRewriteAndAddTemporaryRedirectTest.xml @@ -60,14 +60,14 @@ </actionGroup> <!-- Assert updated product redirect in Store Front--> - <actionGroup ref="StorefrontProductRedirect" stepKey="verifyProductInStoreFront"> + <actionGroup ref="AssertStorefrontProductRedirect" stepKey="verifyProductInStoreFront"> <argument name="productName" value="$$createProduct.name$$"/> <argument name="productSku" value="$$createProduct.sku$$"/> <argument name="productRequestPath" value="{{_defaultProduct.name}}.html"/> </actionGroup> <!-- Assert initial product redirect in Store Front--> - <actionGroup ref="StorefrontProductRedirect" stepKey="verifyProductInStoreFront1"> + <actionGroup ref="AssertStorefrontProductRedirect" stepKey="verifyProductInStoreFront1"> <argument name="productName" value="$$createProduct.name$$"/> <argument name="productSku" value="$$createProduct.sku$$"/> <argument name="productRequestPath" value="$$createProduct.name$$.html"/> From 51aa164598785661a9e8c140f433a0f474f002a2 Mon Sep 17 00:00:00 2001 From: Alex Kolesnyk <kolesnyk@adobe.com> Date: Fri, 22 Feb 2019 10:36:17 -0600 Subject: [PATCH 183/254] Enhance stability of tests using DisableTaxApplyOnOriginalPrice action group --- .../Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml index 510624eb369e2..4e9319351a130 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/ConfigSalesTaxClassActionGroup.xml @@ -36,7 +36,7 @@ <scrollTo selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" x="0" y="-80" stepKey="goToCheckbox"/> <uncheckOption selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" stepKey="enableApplyTaxOnSetting"/> <selectOption selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOn}}" userInput="{{userInput}}" stepKey="setApplyTaxOn"/> - <scrollTo selector="{{SalesConfigSection.TaxClassesTab}}" x="0" y="-80" stepKey="scrollToTop"/> + <scrollTo selector="{{SalesConfigSection.TaxClassesTab}}" stepKey="scrollToTop"/> <click selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" stepKey="collapseCalcSettingsTab"/> <click selector="{{AdminConfigureTaxSection.save}}" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForConfigSaved"/> @@ -49,11 +49,11 @@ <amOnPage url="{{AdminSalesTaxClassPage.url}}" stepKey="navigateToSalesTaxPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <conditionalClick selector="{{AdminConfigureTaxSection.taxCalculationSettings}}" dependentSelector="{{AdminConfigureTaxSection.taxCalculationAlgorithm}}" visible="false" stepKey="openTaxCalcSettingsSection"/> - <scrollTo selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" stepKey="goToCheckbox"/> + <scrollTo selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" x="0" y="-80" stepKey="goToCheckbox"/> <selectOption selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOn}}" userInput="{{userInput}}" stepKey="setApplyTaxOff"/> <checkOption selector="{{AdminConfigureTaxSection.taxCalculationApplyTaxOnInherit}}" stepKey="disableApplyTaxOnSetting"/> <click selector="{{AdminConfigureTaxSection.save}}" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForConfigSaved"/> <see userInput="You saved the configuration." stepKey="seeSuccessMessage"/> </actionGroup> - </actionGroups> +</actionGroups> From 55184cec83116bd462122622464ac7c7ffa23bf2 Mon Sep 17 00:00:00 2001 From: sathakur <sathakur@adobe.com> Date: Fri, 22 Feb 2019 10:47:50 -0600 Subject: [PATCH 184/254] MC-4538: Convert RegisterCustomerFrontendEntityTest to MFTF Addressing more review comments --- .../Mftf/ActionGroup/DeleteCustomerActionGroup.xml | 12 +++++++----- .../Test/Mftf/Section/AdminCustomerGridSection.xml | 1 + .../Test/Mftf/Section/CustomersPageSection.xml | 6 +++--- .../Mftf/Section/StorefrontPanelHeaderSection.xml | 2 +- ...teNewCustomerOnStorefrontSignupNewsletterTest.xml | 6 +++--- .../Test/AdminCreateNewCustomerOnStorefrontTest.xml | 2 +- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml index 7fb9f906474f7..06659dae156a4 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml @@ -24,21 +24,23 @@ <click stepKey="clickOnOk" selector="{{CustomersPageSection.ok}}"/> <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="10"/> </actionGroup> - <actionGroup name="DeleteNewCustomerByEmailActionGroup"> + <actionGroup name="DeleteCustomerByEmailActionGroup"> <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="{{CustomerEntityOne.email}}" stepKey="filterEmail"/> + <fillField selector="{{AdminCustomerFiltersSection.emailInput}}" userInput="{{email}}" stepKey="filterEmail"/> <click selector="{{AdminCustomerFiltersSection.apply}}" stepKey="applyFilter"/> - <waitForPageLoad stepKey="waitForApplyFilters"/> - <checkOption selector="{{CustomersPageSection.customerCheckbox(email)}}" stepKey="seeCustomerEmailInGrid"/> + <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="10"/> + <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="30"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml index d9d3bfe7f737c..7cc32a5fcecd7 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerGridSection.xml @@ -11,5 +11,6 @@ <section name="AdminCustomerGridSection"> <element name="customerGrid" type="text" selector="table[data-role='grid']"/> <element name="firstRowEditLink" type="text" selector="tr[data-repeat-index='0'] .action-menu-item" timeout="30"/> + <element name="selectFirstRow" type="checkbox" selector="//td[@class='data-grid-checkbox-cell']"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml index 60c635387199a..f48a07b5ffd21 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml @@ -11,9 +11,9 @@ <section name="CustomersPageSection"> <element name="addNewCustomerButton" type="button" selector="//*[@id='add']"/> <element name="customerCheckbox" type="button" selector="//*[contains(text(),'{{args}}')]/parent::td/preceding-sibling::td/label[@class='data-grid-checkbox-cell-inner']" parameterized="true"/> - <element name="actions" type="button" selector="//div[@class='col-xs-2']/div[@class='action-select-wrap']/button[@class='action-select']"/> - <element name="delete" type="button" selector="//*[contains(@class,'admin__data-grid-header-row row row-gutter')]//*[text()='Delete']"/> - <element name="ok" type="button" selector="//button[@data-role='action']//span[text()='OK']"/> + <element name="actions" type="button" selector="//div[@class='col-xs-2']/div[@class='action-select-wrap']/button[@class='action-select']" timeout="30"/> + <element name="delete" type="button" selector="//*[contains(@class,'admin__data-grid-header-row row row-gutter')]//*[text()='Delete']" timeout="30"/> + <element name="ok" type="button" selector="//button[@data-role='action']//span[text()='OK']" timeout="30"/> <element name="deletedSuccessMessage" type="button" selector="//*[@class='message message-success success']"/> </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 1955c6a417ba9..eb381519cc351 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontPanelHeaderSection"> <element name="WelcomeMessage" type="text" selector=".greet.welcome span"/> - <element name="createAnAccountLink" type="select" selector=".panel.header li:nth-child(3)" timeout="30"/> + <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"/> <element name="customerWelcome" type="text" selector=".panel.header .customer-welcome"/> <element name="customerWelcomeMenu" type="text" selector=".panel.header .customer-welcome .customer-menu"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml index 081cd684cf2f0..22ad60ff5de34 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontSignupNewsletterTest.xml @@ -23,7 +23,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="DeleteNewCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> @@ -34,7 +34,7 @@ <argument name="customer" value="CustomerEntityOne" /> </actionGroup> - <!--Verify created new customer in grid--> + <!--Assert verify created new customer in grid--> <amOnPage url="{{AdminCustomerPage.url}}" stepKey="navigateToCustomers"/> <waitForPageLoad stepKey="waitForNavigateToCustomersPageLoad"/> <click selector="{{AdminCustomerFiltersSection.filtersButton}}" stepKey="clickFilterButton"/> @@ -44,7 +44,7 @@ <see selector="{{AdminCustomerGridSection.customerGrid}}" userInput="{{CustomerEntityOne.lastname}}" stepKey="seeAssertCustomerLastNameInGrid"/> <see selector="{{AdminCustomerGridSection.customerGrid}}" userInput="{{CustomerEntityOne.email}}" stepKey="seeAssertCustomerEmailInGrid"/> - <!--Verify created new customer is subscribed to newsletter--> + <!--Assert verify created new customer is subscribed to newsletter--> <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickFirstRowEditLink"/> <waitForPageLoad stepKey="waitForEditLinkLoad"/> <click selector="{{AdminEditCustomerInformationSection.newsLetter}}" stepKey="clickNewsLetter"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml index 057590cc3fc39..fc65a271a8196 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateNewCustomerOnStorefrontTest.xml @@ -23,7 +23,7 @@ <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> - <actionGroup ref="DeleteNewCustomerByEmailActionGroup" stepKey="deleteNewUser"> + <actionGroup ref="DeleteCustomerByEmailActionGroup" stepKey="deleteNewUser"> <argument name="email" value="{{CustomerEntityOne.email}}"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> From 83a0ea5d704c34f199cb51631642b20d52f5c0f8 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Fri, 22 Feb 2019 11:14:10 -0600 Subject: [PATCH 185/254] MC-4535: Convert DeleteCustomerBackendEntityTest to MFTF - Remove duplicate DeleteCustomerByEmailActionGroup --- .../AdminDeleteCustomerActionGroup.xml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml index a3334dbd6c842..d08f10b22419d 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminDeleteCustomerActionGroup.xml @@ -21,22 +21,4 @@ <click stepKey="accept" selector="{{AdminCustomerGridMainActionsSection.ok}}"/> <see stepKey="seeSuccessMessage" userInput="were deleted."/> </actionGroup> - <actionGroup name="DeleteCustomerByEmailActionGroup"> - <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"/> - <click selector="{{CustomersPageSection.ok}}" stepKey="clickOkConfirmationButton"/> - <waitForElementVisible stepKey="waitForSuccessfullyDeletedMessage" selector="{{CustomersPageSection.deletedSuccessMessage}}" time="10"/> - </actionGroup> </actionGroups> From f4f62ea72dcd73fc8100ae31b76d46c6d3b7a11d Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Fri, 22 Feb 2019 16:48:38 -0600 Subject: [PATCH 186/254] GraphQL-293: [Payment methods] Set Payment Method on Cart --- app/code/Magento/BraintreeGraphQl/README.md | 4 -- .../Magento/BraintreeGraphQl/composer.json | 24 ------- .../BraintreeGraphQl/etc/graphql/di.xml | 27 -------- .../Magento/BraintreeGraphQl/etc/module.xml | 10 --- .../BraintreeGraphQl/etc/schema.graphqls | 18 ------ .../Magento/BraintreeGraphQl/registration.php | 10 --- .../AdditionalDataBuilderInterface.php | 13 ---- .../Payment/AdditionalDataBuilderPool.php | 32 ---------- .../Payment/DefaultAdditionalDataBuilder.php | 43 ------------- .../Model/Cart/Payment/MethodBuilder.php | 62 ------------------- .../Cart/Payment/PaymentDataProvider.php | 35 ----------- .../Model/Resolver/CartPaymentMethod.php | 58 ----------------- .../Model/Resolver/SelectedPaymentMethod.php | 42 +++++++++++++ .../Model/Resolver/SetPaymentMethodOnCart.php | 49 +++++++++------ .../Resolver/SetShippingAddressesOnCart.php | 9 ++- .../Magento/QuoteGraphQl/etc/schema.graphqls | 30 +++++---- .../Quote/SetPaymentMethodOnCartTest.php | 12 ++-- 17 files changed, 103 insertions(+), 375 deletions(-) delete mode 100644 app/code/Magento/BraintreeGraphQl/README.md delete mode 100644 app/code/Magento/BraintreeGraphQl/composer.json delete mode 100644 app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml delete mode 100644 app/code/Magento/BraintreeGraphQl/etc/module.xml delete mode 100644 app/code/Magento/BraintreeGraphQl/etc/schema.graphqls delete mode 100644 app/code/Magento/BraintreeGraphQl/registration.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderInterface.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderPool.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/DefaultAdditionalDataBuilder.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/MethodBuilder.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/Payment/PaymentDataProvider.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/CartPaymentMethod.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php diff --git a/app/code/Magento/BraintreeGraphQl/README.md b/app/code/Magento/BraintreeGraphQl/README.md deleted file mode 100644 index f6740e4d250e9..0000000000000 --- a/app/code/Magento/BraintreeGraphQl/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# BraintreeGraphQl - -**BraintreeGraphQl** provides type and resolver for method additional -information. \ No newline at end of file diff --git a/app/code/Magento/BraintreeGraphQl/composer.json b/app/code/Magento/BraintreeGraphQl/composer.json deleted file mode 100644 index a322db9d257dc..0000000000000 --- a/app/code/Magento/BraintreeGraphQl/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "magento/module-braintree-graph-ql", - "description": "N/A", - "type": "magento2-module", - "require": { - "php": "~7.1.3||~7.2.0", - "magento/framework": "*" - }, - "suggest": { - "magento/module-graph-ql": "*" - }, - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\BraintreeGraphQl\\": "" - } - } -} diff --git a/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml b/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml deleted file mode 100644 index 3788a0c2f1325..0000000000000 --- a/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml +++ /dev/null @@ -1,27 +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"> - <type name="Magento\QuoteGraphQl\Model\Cart\Payment\AdditionalDataBuilderPool"> - <arguments> - <argument name="builders" xsi:type="array"> - <item name="braintree" xsi:type="object">BraintreeAdditionalDataBuilder</item> - <item name="braintree_vault" xsi:type="object">BraintreeVaultAdditionalDataBuilder</item> - </argument> - </arguments> - </type> - <virtualType name="BraintreeAdditionalDataBuilder" type="Magento\QuoteGraphQl\Model\Cart\Payment\DefaultAdditionalDataBuilder"> - <arguments> - <argument name="methodCode" xsi:type="const">Magento\Braintree\Model\Ui\ConfigProvider::CODE</argument> - </arguments> - </virtualType> - <virtualType name="BraintreeVaultAdditionalDataBuilder" type="Magento\QuoteGraphQl\Model\Cart\Payment\DefaultAdditionalDataBuilder"> - <arguments> - <argument name="methodCode" xsi:type="const">Magento\Braintree\Model\Ui\ConfigProvider::CC_VAULT_CODE</argument> - </arguments> - </virtualType> -</config> diff --git a/app/code/Magento/BraintreeGraphQl/etc/module.xml b/app/code/Magento/BraintreeGraphQl/etc/module.xml deleted file mode 100644 index 2133e95a69104..0000000000000 --- a/app/code/Magento/BraintreeGraphQl/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_BraintreeGraphQl"/> -</config> diff --git a/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls b/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls deleted file mode 100644 index 83c98a68d85ee..0000000000000 --- a/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright © Magento, Inc. All rights reserved. -# See COPYING.txt for license details. - -input PaymentMethodInput { - braintree: BraintreeInput - braintree_vault: BraintreeVaultInput -} - -input BraintreeInput { - payment_method_nonce: String! - is_active_payment_token_enabler: Boolean! -} - -input BraintreeVaultInput { - payment_method_nonce: String! - public_hash: String! - is_active_payment_token_enabler: Boolean! -} diff --git a/app/code/Magento/BraintreeGraphQl/registration.php b/app/code/Magento/BraintreeGraphQl/registration.php deleted file mode 100644 index 37f7ef30864cb..0000000000000 --- a/app/code/Magento/BraintreeGraphQl/registration.php +++ /dev/null @@ -1,10 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -use Magento\Framework\Component\ComponentRegistrar; - -ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_BraintreeGraphQl', __DIR__); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderInterface.php deleted file mode 100644 index 36a5724724a79..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderInterface.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\QuoteGraphQl\Model\Cart\Payment; - -interface AdditionalDataBuilderInterface -{ - public function build(array $args): array; -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderPool.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderPool.php deleted file mode 100644 index dde47507553b5..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataBuilderPool.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\QuoteGraphQl\Model\Cart\Payment; - -class AdditionalDataBuilderPool -{ - /** - * @var AdditionalDataBuilderInterface[] - */ - private $builders = []; - - public function __construct( - array $builders - ) { - $this->builders = $builders; - } - - public function buildForMethod(string $methodCode, array $args): array - { - $additionalData = []; - if (isset($this->builders[$methodCode])) { - $additionalData = $this->builders[$methodCode]->build($args); - } - - return $additionalData; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/DefaultAdditionalDataBuilder.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/DefaultAdditionalDataBuilder.php deleted file mode 100644 index 5a032ccc6386d..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/DefaultAdditionalDataBuilder.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Cart\Payment; - -use Magento\Framework\Stdlib\ArrayManager; - -class DefaultAdditionalDataBuilder implements AdditionalDataBuilderInterface -{ - private const INPUT_PATH_ADDITIONAL_DATA = 'input/payment_method/%s'; - - /** - * @var ArrayManager - */ - private $arrayManager; - - /** - * @var string - */ - private $methodCode; - - public function __construct( - ArrayManager $arrayManager, - string $methodCode = '' - ) { - $this->arrayManager = $arrayManager; - $this->methodCode = $methodCode; - } - - public function build(array $args): array - { - return $this->arrayManager->get($this->getAdditionalDataPath(), $args) ?? []; - } - - private function getAdditionalDataPath(): string - { - return sprintf(static::INPUT_PATH_ADDITIONAL_DATA, $this->methodCode); - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/MethodBuilder.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/MethodBuilder.php deleted file mode 100644 index 110e24d7d0fe5..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/MethodBuilder.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\QuoteGraphQl\Model\Cart\Payment; - -use Magento\Framework\Api\DataObjectHelper; -use Magento\Framework\Stdlib\ArrayManager; -use Magento\Quote\Api\Data\PaymentInterface; -use Magento\Quote\Api\Data\PaymentInterfaceFactory; - -class MethodBuilder -{ - /** - * @var PaymentInterfaceFactory - */ - private $paymentFactory; - - /** - * @var AdditionalDataBuilderPool - */ - private $additionalDataBuilderPool; - - /** - * @var ArrayManager - */ - private $arrayManager; - - /** - * @param PaymentInterfaceFactory $paymentFactory - * @param AdditionalDataBuilderPool $additionalDataBuilderPool - * @param ArrayManager $arrayManager - */ - public function __construct( - PaymentInterfaceFactory $paymentFactory, - AdditionalDataBuilderPool $additionalDataBuilderPool, - ArrayManager $arrayManager - ) { - $this->paymentFactory = $paymentFactory; - $this->additionalDataBuilderPool = $additionalDataBuilderPool; - $this->arrayManager = $arrayManager; - } - - public function build(array $args): PaymentInterface - { - $method = (string) $this->arrayManager->get('input/payment_method/method', $args); - - return $this->paymentFactory->create([ - 'data' => [ - PaymentInterface::KEY_METHOD => $method, - PaymentInterface::KEY_PO_NUMBER => $this->arrayManager->get('input/payment_method/po_number', $args), - PaymentInterface::KEY_ADDITIONAL_DATA => $this->additionalDataBuilderPool->buildForMethod( - $method, - $args - ), - ] - ]); - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/PaymentDataProvider.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/PaymentDataProvider.php deleted file mode 100644 index 22adc69d1cc58..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/PaymentDataProvider.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Cart\Payment; - -use Magento\Quote\Model\Quote; - -/** - * Extract data from payment - */ -class PaymentDataProvider -{ - /** - * Extract data from cart - * - * @param Quote $cart - * @return array - */ - public function getCartPayment(Quote $cart): array - { - $payment = $cart->getPayment(); - if (!$payment) { - return []; - } - - return [ - 'method' => $payment->getMethod(), - 'po_number' => $payment->getPoNumber(), - ]; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartPaymentMethod.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartPaymentMethod.php deleted file mode 100644 index 9e75256cd9b2b..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartPaymentMethod.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Resolver; - -use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\QuoteGraphQl\Model\Cart\Payment\PaymentDataProvider; -use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; - -/** - * @inheritdoc - */ -class CartPaymentMethod implements ResolverInterface -{ - /** - * @var PaymentDataProvider - */ - private $paymentDataProvider; - - /** - * @var GetCartForUser - */ - private $getCartForUser; - - /** - * @param PaymentDataProvider $paymentDataProvider - * @param GetCartForUser $getCartForUser - */ - public function __construct( - PaymentDataProvider $paymentDataProvider, - GetCartForUser $getCartForUser - ) { - $this->paymentDataProvider = $paymentDataProvider; - $this->getCartForUser = $getCartForUser; - } - - /** - * @inheritdoc - */ - public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) - { - if (!isset($value['cart_id'])) { - throw new LocalizedException(__('"model" value should be specified')); - } - - $maskedCartId = $value['cart_id']; - $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); - - return $this->paymentDataProvider->getCartPayment($cart); - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php new file mode 100644 index 0000000000000..5d6aed15821f3 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * @inheritdoc + */ +class SelectedPaymentMethod implements ResolverInterface +{ + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + + /** @var \Magento\Quote\Model\Quote $cart */ + $cart = $value['model']; + + $payment = $cart->getPayment(); + if (!$payment) { + return []; + } + + return [ + 'code' => $payment->getMethod(), + 'po_number' => $payment->getPoNumber(), + ]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php index 5e48b3413f7ce..82709de03b9b6 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php @@ -13,21 +13,16 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; +use Magento\Quote\Api\Data\PaymentInterface; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\Quote\Api\Data\PaymentInterfaceFactory; use Magento\Quote\Api\PaymentMethodManagementInterface; -use Magento\QuoteGraphQl\Model\Cart\Payment\MethodBuilder; /** * Mutation resolver for setting payment method for shopping cart */ class SetPaymentMethodOnCart implements ResolverInterface { - /** - * @var MaskedQuoteIdToQuoteIdInterface - */ - private $maskedQuoteIdToQuoteId; - /** * @var GetCartForUser */ @@ -44,29 +39,26 @@ class SetPaymentMethodOnCart implements ResolverInterface private $paymentMethodManagement; /** - * @var MethodBuilder + * @var PaymentInterfaceFactory */ - private $methodBuilder; + private $paymentFactory; /** - * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId * @param GetCartForUser $getCartForUser * @param ArrayManager $arrayManager * @param PaymentMethodManagementInterface $paymentMethodManagement - * @param MethodBuilder $methodBuilder + * @param PaymentInterfaceFactory $paymentFactory */ public function __construct( - MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, GetCartForUser $getCartForUser, ArrayManager $arrayManager, PaymentMethodManagementInterface $paymentMethodManagement, - MethodBuilder $methodBuilder + PaymentInterfaceFactory $paymentFactory ) { - $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->getCartForUser = $getCartForUser; $this->arrayManager = $arrayManager; $this->paymentMethodManagement = $paymentMethodManagement; - $this->methodBuilder = $methodBuilder; + $this->paymentFactory = $paymentFactory; } /** @@ -74,21 +66,37 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - $paymentMethod = $this->arrayManager->get('input/payment_method', $args); - $maskedCartId = $this->arrayManager->get('input/cart_id', $args); - + $maskedCartId = (string)$this->arrayManager->get('input/cart_id', $args); if (!$maskedCartId) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } + + $paymentMethod = $this->arrayManager->get('input/payment_method', $args); if (!$paymentMethod) { throw new GraphQlInputException(__('Required parameter "payment_method" is missing')); } - $maskedCartId = $args['input']['cart_id']; + $paymentMethodCode = (string) $this->arrayManager->get('input/payment_method/code', $args); + if (!$paymentMethodCode) { + throw new GraphQlInputException(__('Required parameter payment "code" is missing')); + } + + $poNumber = $this->arrayManager->get('input/payment_method/po_number', $args); + if (!$poNumber) { + throw new GraphQlInputException(__('Required parameter payment "po_number" is missing')); + } + $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); + $payment = $this->paymentFactory->create([ + 'data' => [ + PaymentInterface::KEY_METHOD => $paymentMethodCode, + PaymentInterface::KEY_PO_NUMBER => $poNumber, + PaymentInterface::KEY_ADDITIONAL_DATA => [], + ] + ]); try { - $this->paymentMethodManagement->set($cart->getId(), $this->methodBuilder->build($args)); + $this->paymentMethodManagement->set($cart->getId(), $payment); } catch (LocalizedException $e) { throw new GraphQlInputException(__($e->getMessage())); } @@ -96,6 +104,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return [ 'cart' => [ 'cart_id' => $maskedCartId, + 'model' => $cart, ], ]; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php index ec50bd6ab6ea4..a55e2971e0ef7 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php @@ -7,6 +7,7 @@ namespace Magento\QuoteGraphQl\Model\Resolver; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -67,7 +68,7 @@ public function __construct( public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { $shippingAddresses = $this->arrayManager->get('input/shipping_addresses', $args); - $maskedCartId = (string) $this->arrayManager->get('input/cart_id', $args); + $maskedCartId = (string)$this->arrayManager->get('input/cart_id', $args); if (!$maskedCartId) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); @@ -79,7 +80,11 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); - $this->setShippingAddressesOnCart->execute($context, $cart, $shippingAddresses); + try { + $this->setShippingAddressesOnCart->execute($context, $cart, $shippingAddresses); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } return [ 'cart' => [ diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 2a83b2969d73d..78c12f939d8fb 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -109,8 +109,12 @@ input SetPaymentMethodOnCartInput { } input PaymentMethodInput { - method: String! - po_number: String + code: String! @doc(description:"Payment method code") + po_number: String! @doc(description:"Purchase order number") + additional_data: PaymentMethodAdditionalDataInput +} + +input PaymentMethodAdditionalDataInput { } type SetPaymentMethodOnCartOutput { @@ -139,8 +143,8 @@ type Cart { applied_coupon: AppliedCoupon shipping_addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddresses") billing_address: CartAddress! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\BillingAddress") - available_payment_methods : [PaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods") - payment_method: CartPaymentMethod @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartPaymentMethod") + available_payment_methods : [AvailablePaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods") + selected_payment_method: SelectedPaymentMethod @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SelectedPaymentMethod") } type CartAddress { @@ -154,18 +158,13 @@ type CartAddress { country: CartAddressCountry telephone: String address_type: AdressTypeEnum - selected_shipping_method: SelectedShippingMethod available_shipping_methods: [AvailableShippingMethod] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAdress\\AvailableShippingMethods") + selected_shipping_method: SelectedShippingMethod items_weight: Float customer_notes: String cart_items: [CartItemQuantity] } -type CartPaymentMethod { - method: String - po_number: String -} - type CartItemQuantity { cart_item_id: String! quantity: Float! @@ -197,11 +196,20 @@ type AvailableShippingMethod { price_incl_tax: Float! } -type PaymentMethod { +type AvailablePaymentMethod { code: String @doc(description: "The payment method code") title: String @doc(description: "The payment method title.") } +type SelectedPaymentMethod { + code: String @doc(description: "The payment method code") + po_number: String @doc(description: "The purchase order number.") + additional_data: SelectedPaymentMethodAdditionalData +} + +type SelectedPaymentMethodAdditionalData { +} + enum AdressTypeEnum { SHIPPING BILLING diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php index cd16123d428b0..496c3efbbd898 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php @@ -142,7 +142,7 @@ public function testSetPaymentMethodOnCart(string $methodCode) $this->assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); $this->assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); $this->assertArrayHasKey('payment_method', $response['setPaymentMethodOnCart']['cart']); - $this->assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['payment_method']['method']); + $this->assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['payment_method']['code']); } public function dataProviderOfflinePaymentMethods(): array @@ -236,7 +236,7 @@ public function testSetPaymentMethodPurchaseOrderOnCart() { cart_id: "$maskedQuoteId", payment_method: { - method: "$methodCode" + code: "$methodCode" po_number: "$poNumber" } }) { @@ -244,7 +244,7 @@ public function testSetPaymentMethodPurchaseOrderOnCart() cart { cart_id, payment_method { - method + code po_number } } @@ -259,7 +259,7 @@ public function testSetPaymentMethodPurchaseOrderOnCart() $this->assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); $this->assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); $this->assertArrayHasKey('payment_method', $response['setPaymentMethodOnCart']['cart']); - $this->assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['payment_method']['method']); + $this->assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['payment_method']['code']); $this->assertEquals($poNumber, $response['setPaymentMethodOnCart']['cart']['payment_method']['po_number']); } @@ -301,14 +301,14 @@ private function prepareMutationQuery( { cart_id: "$maskedQuoteId", payment_method: { - method: "$methodCode" + code: "$methodCode" } }) { cart { cart_id, payment_method { - method + code } } } From 7871f4c973102ef12af1748ff296bc4859aa0049 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 22 Feb 2019 20:26:26 -0500 Subject: [PATCH 187/254] Save country when creating new quote addresses Fixes #389 --- .../Model/Cart/SetBillingAddressOnCart.php | 1 + .../Model/Cart/SetShippingAddressOnCart.php | 1 + .../Quote/SetBillingAddressOnCartTest.php | 19 ++++++++++++++++++- .../Quote/SetShippingAddressOnCartTest.php | 11 ++++++++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 5d3125ae13ef8..02aec5b6fbaf0 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -93,6 +93,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $b ); } if (null === $customerAddressId) { + $addressInput['country_id'] = $addressInput['country_code'] ?? ''; $billingAddress = $this->addressModel->addData($addressInput); } else { $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType()); diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php index e6b18fc88a27a..fc8eff4cfc13a 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php @@ -87,6 +87,7 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s ); } if (null === $customerAddressId) { + $addressInput['country_id'] = $addressInput['country_code'] ?? ''; $shippingAddress = $this->addressModel->addData($addressInput); } else { $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType()); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php index bfe57109d107d..754b157a16107 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php @@ -88,6 +88,10 @@ public function testSetNewBillingAddressByGuest() city postcode telephone + country { + code + label + } } } } @@ -140,6 +144,10 @@ public function testSetNewBillingAddressWithUseForShippingParameterByGuest() city postcode telephone + country { + code + label + } } shipping_addresses { firstname @@ -149,6 +157,10 @@ public function testSetNewBillingAddressWithUseForShippingParameterByGuest() city postcode telephone + country { + code + label + } } } } @@ -234,6 +246,10 @@ public function testSetNewBillingAddressByRegisteredCustomer() city postcode telephone + country { + code + label + } } } } @@ -413,7 +429,8 @@ private function assertNewAddressFields(array $billingAddressResponse): void ['response_field' => 'street', 'expected_value' => [0 => 'test street 1', 1 => 'test street 2']], ['response_field' => 'city', 'expected_value' => 'test city'], ['response_field' => 'postcode', 'expected_value' => '887766'], - ['response_field' => 'telephone', 'expected_value' => '88776655'] + ['response_field' => 'telephone', 'expected_value' => '88776655'], + ['response_field' => 'country', 'expected_value' => ['code' => 'US', 'label' => 'US']], ]; $this->assertResponseFields($billingAddressResponse, $assertionMap); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php index c3685c88ae487..4d7ab85cd475a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -90,6 +90,10 @@ public function testSetNewShippingAddressByGuest() city postcode telephone + country { + code + label + } } } } @@ -176,6 +180,10 @@ public function testSetNewShippingAddressByRegisteredCustomer() city postcode telephone + country { + label + code + } } } } @@ -462,7 +470,8 @@ private function assertNewShippingAddressFields(array $shippingAddressResponse): ['response_field' => 'street', 'expected_value' => [0 => 'test street 1', 1 => 'test street 2']], ['response_field' => 'city', 'expected_value' => 'test city'], ['response_field' => 'postcode', 'expected_value' => '887766'], - ['response_field' => 'telephone', 'expected_value' => '88776655'] + ['response_field' => 'telephone', 'expected_value' => '88776655'], + ['response_field' => 'country', 'expected_value' => ['code' => 'US', 'label' => 'US']], ]; $this->assertResponseFields($shippingAddressResponse, $assertionMap); From 5c3cb172328d6f36348e334ee87ecd8a1e69c1db Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 22 Feb 2019 22:30:48 -0500 Subject: [PATCH 188/254] Add cart address id to shipping address This is required for setting quote address shipping methods --- .../Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php | 1 + app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php index b0e5070315d87..89aa943f9d211 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php @@ -41,6 +41,7 @@ public function execute(QuoteAddress $address): array $addressData['model'] = $address; $addressData = array_merge($addressData, [ + 'address_id' => $address->getId(), 'country' => [ 'code' => $address->getCountryId(), 'label' => $address->getCountry() diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 2050a2d3ca790..a9a609b01f8c0 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -128,6 +128,7 @@ type Cart { } type CartAddress { + address_id: Int firstname: String lastname: String company: String From 9c4241e3fae07242bfdc1e554c9cb54a11f9ff28 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 22 Feb 2019 22:31:54 -0500 Subject: [PATCH 189/254] Correct validation of shipping method input in resolver Correlates to the schema changes made: https://github.com/magento/graphql-ce/commit/fa5613bb8ef5d85fdb75a70854f0caf69e58c4fd --- .../Model/Resolver/SetShippingMethodsOnCart.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php index 920829f5d67b1..67947e928796c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php @@ -72,10 +72,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!$shippingMethod['cart_address_id']) { throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); } - if (!$shippingMethod['shipping_carrier_code']) { + if (!$shippingMethod['carrier_code']) { throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - if (!$shippingMethod['shipping_method_code']) { + if (!$shippingMethod['method_code']) { throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); } @@ -85,8 +85,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $this->setShippingMethodOnCart->execute( $cart, $shippingMethod['cart_address_id'], - $shippingMethod['shipping_carrier_code'], - $shippingMethod['shipping_method_code'] + $shippingMethod['carrier_code'], + $shippingMethod['method_code'] ); return [ From 75573a9e28cd3a893582572d08846b2d06437c47 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Wed, 13 Feb 2019 23:19:41 -0500 Subject: [PATCH 190/254] [Cart Operations] Remove item mutation Partial #37 --- .../Resolver/RemoveItemFromCartOutput.php | 73 ++++++ .../Magento/QuoteGraphQl/etc/schema.graphqls | 10 + .../GraphQl/Quote/RemoveItemFromCartTest.php | 227 ++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCartOutput.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCartOutput.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCartOutput.php new file mode 100644 index 0000000000000..658f670fd6825 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCartOutput.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Config\Element\Field; +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; +use Magento\Quote\Api\GuestCartItemRepositoryInterface; +use Magento\Quote\Api\GuestCartRepositoryInterface; +use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromCart; + +/** + * @inheritdoc + */ +class RemoveItemFromCartOutput implements ResolverInterface +{ + /** + * @var GuestCartItemRepositoryInterface + */ + private $guestCartItemRepository; + + /** + * @var GuestCartRepositoryInterface + */ + private $guestCartRepository; + + /** + * @var ExtractDataFromCart + */ + private $extractDataFromCart; + + public function __construct( + GuestCartItemRepositoryInterface $guestCartItemRepository, + GuestCartRepositoryInterface $guestCartRepository, + ExtractDataFromCart $extractDataFromCart + ) { + $this->guestCartItemRepository = $guestCartItemRepository; + $this->guestCartRepository = $guestCartRepository; + $this->extractDataFromCart = $extractDataFromCart; + } + + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($args['input']['cart_id'])) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + } + if (!isset($args['input']['cart_item_id'])) { + throw new GraphQlInputException(__('Required parameter "cart_item_id" is missing')); + } + $maskedCartId = $args['input']['cart_id']; + $itemId = $args['input']['cart_item_id']; + + try { + $this->guestCartItemRepository->deleteById($maskedCartId, $itemId); + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException(__($e->getMessage())); + } + + $cart = $this->guestCartRepository->get($maskedCartId); + + $cartData = $this->extractDataFromCart->execute($cart); + + return ['cart' => array_merge(['cart_id' => $maskedCartId], $cartData)]; + } +} diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 2050a2d3ca790..b2e338bf4edf5 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -14,6 +14,7 @@ type Mutation { setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingAddressesOnCart") setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetBillingAddressOnCart") setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart") + removeItemFromCart(input: RemoveItemFromCartInput): RemoveItemFromCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveItemFromCartOutput") } input AddSimpleProductsToCartInput { @@ -206,6 +207,15 @@ type AddVirtualProductsToCartOutput { cart: Cart! } +input RemoveItemFromCartInput { + cart_id: String! + cart_item_id: String! +} + +type RemoveItemFromCartOutput { + cart: Cart! +} + type SimpleCartItem implements CartItemInterface @doc(description: "Simple Cart Item") { customizable_options: [SelectedCustomizableOption] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\CustomizableOptions") } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php new file mode 100644 index 0000000000000..9a949af5bc1e1 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php @@ -0,0 +1,227 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\GuestCartRepositoryInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\TestFramework\ObjectManager; + +/** + * Test for empty cart creation mutation + */ +class RemoveItemFromCartTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var Quote + */ + private $quote; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var GuestCartRepositoryInterface + */ + private $guestCartRepository; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->create(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->productRepository = $objectManager->create(ProductRepositoryInterface::class); + $this->guestCartRepository = $objectManager->create(GuestCartRepositoryInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testGuestRemoveItemFromCart() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $itemId = $this->quote->getItemByProduct($this->productRepository->get('simple'))->getId(); + + $query = <<<QUERY +mutation { + removeItemFromCart( + input: { + cart_id: "$maskedQuoteId" + cart_item_id: "$itemId" + } + ) { + cart { + cart_id + items { + id + qty + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('removeItemFromCart', $response); + $this->assertArrayHasKey('cart', $response['removeItemFromCart']); + + $responseCart = $response['removeItemFromCart']['cart']; + + $this->assertCount(0, $responseCart['items']); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testRemoveItemFromCart() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $itemId = $this->quote->getItemByProduct($this->productRepository->get('simple'))->getId(); + + $this->quote->setCustomerId(1); + $this->quoteResource->save($this->quote); + + $headerMap = $this->getHeaderMap(); + + $query = <<<QUERY +mutation { + removeItemFromCart( + input: { + cart_id: "$maskedQuoteId" + cart_item_id: "$itemId" + } + ) { + cart { + cart_id + items { + id + qty + } + } + } +} +QUERY; + + $response = $this->graphQlQuery($query, [], '', $headerMap); + + $this->assertArrayHasKey('removeItemFromCart', $response); + $this->assertArrayHasKey('cart', $response['removeItemFromCart']); + + $responseCart = $response['removeItemFromCart']['cart']; + + $this->assertCount(0, $responseCart['items']); + } + + public function testRemoveItemFromCartNoSuchCartIdException() + { + $maskedCartId = 'nada'; + + $this->expectExceptionMessage('No such entity with cartId'); + + $query = <<<QUERY +mutation { + removeItemFromCart( + input: { + cart_id: "$maskedCartId" + cart_item_id: "nononono" + } + ) { + cart { + cart_id + items { + id + qty + } + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testRemoveItemFromCartNoSuchCartItem() + { + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $itemId = 'nononono'; + + $this->expectExceptionMessage(sprintf('Cart doesn\'t contain the %s item.', $itemId)); + + $query = <<<QUERY +mutation { + removeItemFromCart( + input: { + cart_id: "$maskedQuoteId" + cart_item_id: "$itemId" + } + ) { + cart { + cart_id + items { + id + qty + } + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * @param string $username + * @return array + */ + private function getHeaderMap(string $username = 'customer@example.com'): array + { + $password = 'password'; + /** @var CustomerTokenServiceInterface $customerTokenService */ + $customerTokenService = ObjectManager::getInstance() + ->get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } +} From beec4a410081bb7ebf2e39dfdbf1a8ebfaff2e80 Mon Sep 17 00:00:00 2001 From: Patrick McLain <pat@pmclain.com> Date: Fri, 22 Feb 2019 08:06:25 -0500 Subject: [PATCH 191/254] Functional test validating only items from requested cart can be removed --- .../GraphQl/Quote/RemoveItemFromCartTest.php | 70 +++++++++++++++++-- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php index 9a949af5bc1e1..d7d3d5c8b9e92 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php @@ -9,7 +9,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Quote\Api\Data\CartInterface; +use Magento\Framework\ObjectManagerInterface; use Magento\Quote\Api\GuestCartRepositoryInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; @@ -23,6 +23,11 @@ */ class RemoveItemFromCartTest extends GraphQlAbstract { + /** + * @var ObjectManagerInterface + */ + private $objectManager; + /** * @var QuoteResource */ @@ -50,12 +55,12 @@ class RemoveItemFromCartTest extends GraphQlAbstract protected function setUp() { - $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->create(QuoteResource::class); - $this->quote = $objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); - $this->productRepository = $objectManager->create(ProductRepositoryInterface::class); - $this->guestCartRepository = $objectManager->create(GuestCartRepositoryInterface::class); + $this->objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $this->objectManager->create(QuoteResource::class); + $this->quote = $this->objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $this->objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->guestCartRepository = $this->objectManager->create(GuestCartRepositoryInterface::class); } /** @@ -210,6 +215,57 @@ public function testRemoveItemFromCartNoSuchCartItem() $this->graphQlQuery($query); } + /** + * Test mutation is only able to remove quote item belonging to the requested cart + * + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage Could not find cart item with id + */ + public function testRemoveItemFromDifferentQuote() + { + /** @var Quote $secondQuote */ + $secondQuote = $this->objectManager->create(Quote::class); + $this->quoteResource->load( + $secondQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); + $secondQuoteItem = $secondQuote->getItemByProduct($this->productRepository->get('virtual-product')); + + $this->quoteResource->load( + $this->quote, + 'test_order_with_simple_product_without_address', + 'reserved_order_id' + ); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $itemId = $secondQuoteItem->getItemId(); + + $this->expectExceptionMessage(sprintf('Cart doesn\'t contain the %s item.', $itemId)); + + $query = <<<QUERY +mutation { + removeItemFromCart( + input: { + cart_id: "$maskedQuoteId" + cart_item_id: "$itemId" + } + ) { + cart { + cart_id + items { + id + qty + } + } + } +} +QUERY; + + $this->graphQlQuery($query); + } + /** * @param string $username * @return array From ee1719231a31e9a0d7930f07657fde8e944664f9 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Tue, 19 Feb 2019 16:44:12 +0000 Subject: [PATCH 192/254] magento/magento2#20621: Removed deprecated method --- .../Mail/Template/TransportBuilder.php | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 0d69b3d96cebf..b9271c0209fd3 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -183,33 +183,20 @@ public function setReplyTo($email, $name = null) * * @param string|array $from * @return $this + * @throws \Magento\Framework\Exception\MailException */ public function setFrom($from) { return $this->setFromByScope($from, null); } - /** - * Set mail from address by store - * - * @deprecated Use setFromByScope - * @see setFromByScope() - * - * @param string|array $from - * @param string|int $store - * @return $this - */ - public function setFromByStore($from, $store = null) - { - return $this->setFromByScope($from, $store); - } - /** * Set mail from address by scopeId * * @param string|array $from * @param string|int $scopeId * @return $this + * @throws \Magento\Framework\Exception\MailException */ public function setFromByScope($from, $scopeId = null) { @@ -270,6 +257,7 @@ public function setTemplateOptions($templateOptions) * Get mail transport * * @return \Magento\Framework\Mail\TransportInterface + * @throws LocalizedException */ public function getTransport() { From d78e69a120e135af038d9dd21a99ab708159d911 Mon Sep 17 00:00:00 2001 From: Cyanoxide <cyanoxide@gmail.com> Date: Sun, 24 Feb 2019 20:20:38 +0000 Subject: [PATCH 193/254] Improve swatch table overflow handling Add overflow:auto to the three swatch tables to allow for scrolling if the amount of inputs exceeds the available space and increase the min width of inputs (150px) within the table for better usability. Intentionally left original input width (50px) as a fallback. --- .../Swatches/view/adminhtml/web/css/swatches.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css b/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css index ef635c48e3466..b0ea10b1ed968 100644 --- a/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css +++ b/app/code/Magento/Swatches/view/adminhtml/web/css/swatches.css @@ -153,6 +153,18 @@ min-width: 65px; } +[class^=swatch-col], +[class^=col-]:not(.col-draggable):not(.col-default) { + min-width: 150px; +} + +#swatch-visual-options-panel, +#swatch-text-options-panel, +#manage-options-panel { + overflow: auto; + width: 100%; +} + .data-table .col-swatch-min-width input[type="text"] { padding: inherit; } From d846ca1361fd4fb30c40b8a349ec9047c9708dc1 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <l.lesechko@gmail.com> Date: Sun, 24 Feb 2019 23:29:26 -0600 Subject: [PATCH 194/254] All requested changes are applied --- .../ClearShippingAddressActionGroup.xml | 21 ++++++++++++++++ ...illShippingAddressOneStreetActionGroup.xml | 24 +++++++++++++++++++ .../FillShippingZipFormActionGroup.xml | 23 ------------------ ...frontAddSimpleProductToCartActionGroup.xml | 22 +++++++++++++++++ .../StorefrontProductCartActionGroup.xml | 10 -------- ...ditShippingAddressOnePageCheckoutTest.xml} | 10 ++++---- ...EditShippingAddressOnePageCheckoutTest.xml | 3 +-- 7 files changed, 73 insertions(+), 40 deletions(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClearShippingAddressActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingAddressOneStreetActionGroup.xml create mode 100644 app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml rename app/code/Magento/Checkout/Test/Mftf/Test/{EditShippingAddressOnePageCheckoutTestVariation1.xml => EditShippingAddressOnePageCheckoutTest.xml} (91%) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClearShippingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClearShippingAddressActionGroup.xml new file mode 100644 index 0000000000000..8c6ae3de4f2f4 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClearShippingAddressActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details.z + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="ClearShippingAddressActionGroup"> + <clearField selector="{{CheckoutShippingSection.firstName}}" stepKey="clearFieldFirstName"/> + <clearField selector="{{CheckoutShippingSection.company}}" stepKey="clearFieldCompany"/> + <clearField selector="{{CheckoutShippingSection.street}}" stepKey="clearFieldStreetAddress"/> + <clearField selector="{{CheckoutShippingSection.city}}" stepKey="clearFieldCityName"/> + <selectOption selector="{{CheckoutShippingSection.region}}" userInput="" stepKey="clearFieldRegion"/> + <clearField selector="{{CheckoutShippingSection.postcode}}" stepKey="clearFieldZip"/> + <selectOption selector="{{CheckoutShippingSection.country}}" userInput="" stepKey="clearFieldCounty"/> + <clearField selector="{{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingAddressOneStreetActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingAddressOneStreetActionGroup.xml new file mode 100644 index 0000000000000..ac877b01e743e --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingAddressOneStreetActionGroup.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="FillShippingAddressOneStreetActionGroup"> + <arguments> + <argument name="address" type="entity"/> + </arguments> + <fillField stepKey="fillFirstName" selector="{{CheckoutShippingSection.firstName}}" userInput="{{address.firstname}}"/> + <fillField stepKey="fillLastName" selector="{{CheckoutShippingSection.lastName}}" userInput="{{address.lastname}}"/> + <fillField stepKey="fillCompany" selector="{{CheckoutShippingSection.company}}" userInput="{{address.company}}"/> + <fillField stepKey="fillPhoneNumber" selector="{{CheckoutShippingSection.telephone}}" userInput="{{address.telephone}}"/> + <fillField stepKey="fillStreetAddress" selector="{{CheckoutShippingSection.street}}" userInput="{{address.street[0]}}"/> + <fillField stepKey="fillCityName" selector="{{CheckoutShippingSection.city}}" userInput="{{address.city}}"/> + <selectOption stepKey="selectCounty" selector="{{CheckoutShippingSection.country}}" userInput="{{address.country_id}}"/> + <fillField stepKey="fillZip" selector="{{CheckoutShippingSection.postcode}}" userInput="{{address.postcode}}"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml index 1278ba3afb85b..f12bf4344ab12 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingZipFormActionGroup.xml @@ -20,27 +20,4 @@ <fillField stepKey="fillPostCode" selector="{{CheckoutCartSummarySection.postcode}}" userInput="{{address.postcode}}"/> <waitForPageLoad stepKey="waitForFormUpdate"/> </actionGroup> - <actionGroup name="FillShippingAddressOneStreetActionGroup"> - <arguments> - <argument name="Address"/> - </arguments> - <fillField stepKey="fillFirstName" selector="{{CheckoutShippingSection.firstName}}" userInput="{{Address.firstname}}"/> - <fillField stepKey="fillLastName" selector="{{CheckoutShippingSection.lastName}}" userInput="{{Address.lastname}}"/> - <fillField stepKey="fillCompany" selector="{{CheckoutShippingSection.company}}" userInput="{{Address.company}}"/> - <fillField stepKey="fillPhoneNumber" selector="{{CheckoutShippingSection.telephone}}" userInput="{{Address.telephone}}"/> - <fillField stepKey="fillStreetAddress" selector="{{CheckoutShippingSection.street}}" userInput="{{Address.street[0]}}"/> - <fillField stepKey="fillCityName" selector="{{CheckoutShippingSection.city}}" userInput="{{Address.city}}"/> - <selectOption stepKey="selectCounty" selector="{{CheckoutShippingSection.country}}" userInput="{{Address.country_id}}"/> - <fillField stepKey="fillZip" selector="{{CheckoutShippingSection.postcode}}" userInput="{{Address.postcode}}"/> - </actionGroup> - <actionGroup name="clearShippingAddressActionGroup"> - <clearField selector="{{CheckoutShippingSection.firstName}}" stepKey="clearFieldFirstName"/> - <clearField selector="{{CheckoutShippingSection.company}}" stepKey="clearFieldCompany"/> - <clearField selector="{{CheckoutShippingSection.street}}" stepKey="clearFieldStreetAddress"/> - <clearField selector="{{CheckoutShippingSection.city}}" stepKey="clearFieldCityName"/> - <selectOption selector="{{CheckoutShippingSection.region}}" userInput="" stepKey="clearFieldRegion"/> - <clearField selector="{{CheckoutShippingSection.postcode}}" stepKey="clearFieldZip"/> - <selectOption selector="{{CheckoutShippingSection.country}}" userInput="" stepKey="clearFieldCounty"/> - <clearField selector="{{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.xml new file mode 100644 index 0000000000000..7bfc87cd8d6f9 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddSimpleProductToCartActionGroup.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"> + <!-- Add Product to Cart from the category page and check message --> + <actionGroup name="StorefrontAddSimpleProductToCartActionGroup"> + <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" /> + <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added {{product.name}} to your shopping cart." stepKey="assertSuccessMessage"/> + </actionGroup> +</actionGroups> + diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index fbba37c22c8af..24ed05583b6fb 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -112,14 +112,4 @@ <fillField stepKey="fillZip" selector="{{CheckoutCartSummarySection.postcode}}" userInput="{{taxCode.zip}}"/> <waitForPageLoad stepKey="waitForFormUpdate"/> </actionGroup> - <!-- Add Product to Cart from the category page and check message --> - <actionGroup name="StorefrontAddCategorySimpleProductToCartActionGroup"> - <arguments> - <argument name="product"/> - </arguments> - <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct" /> - <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="clickAddToCart" /> - <waitForElementVisible selector="{{StorefrontCategoryMainSection.SuccessMsg}}" stepKey="waitForSuccessMessage" /> - <see selector="{{StorefrontCategoryMainSection.SuccessMsg}}" userInput="You added {{product.name}} to your shopping cart." stepKey="assertSuccessMessage"/> - </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTestVariation1.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTest.xml similarity index 91% rename from app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTestVariation1.xml rename to app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTest.xml index d1eb09af976c5..20015f76e08e3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTestVariation1.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EditShippingAddressOnePageCheckoutTest.xml @@ -8,14 +8,14 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="EditShippingAddressOnePageCheckoutTestVariation1"> + <test name="EditShippingAddressOnePageCheckoutTest"> <annotations> <features value="Checkout"/> <stories value="Edit Shipping Address"/> <title value="Edit Shipping Address on Checkout Page."/> <description value="Edit Shipping Address on Checkout Page."/> <severity value="MAJOR"/> - <testCaseId value="MAGETWO-67837"/> + <testCaseId value="MC-14680"/> <group value="shoppingCart"/> <group value="mtf_migrated"/> </annotations> @@ -39,7 +39,7 @@ <!-- Add product to cart --> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> - <actionGroup ref="StorefrontAddCategorySimpleProductToCartActionGroup" stepKey="addProductToCart"> + <actionGroup ref="StorefrontAddSimpleProductToCartActionGroup" stepKey="addProductToCart"> <argument name="product" value="$$createProduct$$"/> </actionGroup> @@ -51,7 +51,7 @@ <!--Fill in required fields and click *Save address* button--> <actionGroup ref="FillShippingAddressOneStreetActionGroup" stepKey="changeAddress"> - <argument name="Address" value="UK_Not_Default_Address"/> + <argument name="address" value="UK_Not_Default_Address"/> </actionGroup> <click selector="{{CheckoutShippingSection.saveAddress}}" stepKey="saveNewAddress"/> @@ -63,7 +63,7 @@ <click selector="{{CheckoutShippingSection.editActiveAddress}}" stepKey="editNewAddress"/> <!--Remove values from required fields and click *Cancel* button --> - <actionGroup ref="clearShippingAddressActionGroup" stepKey="clearRequiredFields"/> + <actionGroup ref="ClearShippingAddressActionGroup" stepKey="clearRequiredFields"/> <click selector="{{CheckoutShippingSection.cancelChangeAddress}}" stepKey="cancelEditAddress"/> <!-- Go to *Next* --> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml index 51bcad08ce349..64ed05904469e 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/EditShippingAddressOnePageCheckoutTest.xml @@ -8,8 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Checkout\Test\TestCase\EditShippingAddressOnePageCheckoutTest" summary="Customer can place order with new addresses that was edited during checkout with several conditions" ticketId="MAGETWO-67837"> <variation name="EditShippingAddressOnePageCheckoutTestVariation1"> - <data name="tag" xsi:type="string">mftf_migrated:yes</data> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="customer/dataset" xsi:type="string">johndoe_with_addresses</data> <data name="shippingAddress/dataset" xsi:type="string">UK_address_without_email</data> <data name="editShippingAddress/dataset" xsi:type="string">empty_UK_address_without_email</data> From 161a513f43b27c42e9cabcfc9433d7881d410403 Mon Sep 17 00:00:00 2001 From: Mila Lesechko <l.lesechko@gmail.com> Date: Sun, 24 Feb 2019 23:42:40 -0600 Subject: [PATCH 195/254] Convert EditShippingAddressOnePageCheckoutTest to MFTF --- .../Test/Mftf/ActionGroup/ClearShippingAddressActionGroup.xml | 2 +- .../ActionGroup/FillShippingAddressOneStreetActionGroup.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClearShippingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClearShippingAddressActionGroup.xml index 8c6ae3de4f2f4..0e6994e8feaa4 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClearShippingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/ClearShippingAddressActionGroup.xml @@ -18,4 +18,4 @@ <selectOption selector="{{CheckoutShippingSection.country}}" userInput="" stepKey="clearFieldCounty"/> <clearField selector="{{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingAddressOneStreetActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingAddressOneStreetActionGroup.xml index ac877b01e743e..cbc6c5320b01c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingAddressOneStreetActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/FillShippingAddressOneStreetActionGroup.xml @@ -21,4 +21,4 @@ <selectOption stepKey="selectCounty" selector="{{CheckoutShippingSection.country}}" userInput="{{address.country_id}}"/> <fillField stepKey="fillZip" selector="{{CheckoutShippingSection.postcode}}" userInput="{{address.postcode}}"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> From c6a17df232e62341d50085dd2e437b5c2c9b18c3 Mon Sep 17 00:00:00 2001 From: priti <priti@2jcommerce.in> Date: Mon, 25 Feb 2019 11:42:39 +0530 Subject: [PATCH 196/254] fixes-for-product-page-product-in-website-multi-store-view-not-display-proper --- .../backend/web/css/source/forms/fields/_control-table.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less index a9035a9a7e47d..697d11fb57d67 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/fields/_control-table.less @@ -118,7 +118,7 @@ } &._fit { - width: 1px; + width: auto; } } From 704065a0183d832df027b4f4cb0fc39608a2342d Mon Sep 17 00:00:00 2001 From: Daniel Ruf <daniel.ruf@ueberbit.de> Date: Mon, 25 Feb 2019 10:53:52 +0100 Subject: [PATCH 197/254] Fix grammar --- app/code/Magento/Backend/etc/adminhtml/system.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 0fb7d89f924de..a52033b8d2501 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -112,7 +112,7 @@ <group id="debug" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Debug</label> <field id="template_hints_storefront" translate="label" type="select" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> - <label>Enabled Template Path Hints for Storefront</label> + <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"> @@ -132,7 +132,7 @@ <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"> - <label>Enabled Template Path Hints for Admin</label> + <label>Enable Template Path Hints for Admin</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> <field id="template_hints_blocks" translate="label" type="select" sortOrder="21" showInDefault="1" showInWebsite="1" showInStore="1"> From d4a5b46a2374f973be97eb47cd37e2413dd2acfb Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 25 Feb 2019 13:42:53 +0200 Subject: [PATCH 198/254] Fix static tests. --- app/code/Magento/Checkout/Controller/Cart/Addgroup.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php index f32fd9504c77d..7eb9362031258 100644 --- a/app/code/Magento/Checkout/Controller/Cart/Addgroup.php +++ b/app/code/Magento/Checkout/Controller/Cart/Addgroup.php @@ -4,17 +4,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Controller\Cart; use Magento\Checkout\Model\Cart as CustomerCart; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Escaper; use Magento\Framework\App\ObjectManager; use Magento\Sales\Model\Order\Item; /** + * Add grouped items controller. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Addgroup extends \Magento\Checkout\Controller\Cart +class Addgroup extends \Magento\Checkout\Controller\Cart implements HttpPostActionInterface { /** * @var Escaper @@ -44,6 +48,8 @@ public function __construct( } /** + * Add items in group. + * * @return \Magento\Framework\Controller\Result\Redirect */ public function execute() From 948f83deefbd6a60447ba5b592307d2567c7d2f7 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 25 Feb 2019 14:11:09 +0200 Subject: [PATCH 199/254] Fix static tests. --- app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php | 5 +++++ app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php b/app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php index 6483ec8be6335..28a6baa436ef6 100644 --- a/app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php +++ b/app/code/Magento/GiftMessage/Block/Cart/GiftOptions.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\GiftMessage\Block\Cart; use Magento\Backend\Block\Template\Context; @@ -10,6 +11,8 @@ use Magento\GiftMessage\Model\CompositeConfigProvider; /** + * Gift options cart block. + * * @api * @since 100.0.2 */ @@ -63,6 +66,8 @@ public function __construct( } /** + * Retrieve encoded js layout. + * * @return string */ public function getJsLayout() diff --git a/app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php b/app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php index d09f882820a92..e1c8c0b5bf5a5 100644 --- a/app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php +++ b/app/code/Magento/GiftMessage/Model/Type/Plugin/Onepage.php @@ -3,8 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\GiftMessage\Model\Type\Plugin; +/** + * Add gift message to quote plugin. + */ class Onepage { /** @@ -30,6 +34,8 @@ public function __construct( } /** + * Add gift message ot quote. + * * @param \Magento\Checkout\Model\Type\Onepage $subject * @param array $result * @return array From e9f4981181d13e33c286e293ab6829f9db504780 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 25 Feb 2019 15:12:22 +0200 Subject: [PATCH 200/254] Fix static tests. --- .../Controller/Adminhtml/Sitemap/Save.php | 39 +++++++++++-------- .../Controller/Adminhtml/Sitemap/SaveTest.php | 3 ++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php index d0cb650028e99..5230de0429778 100644 --- a/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php +++ b/app/code/Magento/Sitemap/Controller/Adminhtml/Sitemap/Save.php @@ -5,11 +5,18 @@ */ namespace Magento\Sitemap\Controller\Adminhtml\Sitemap; -use Magento\Backend\App\Action; +use Magento\Backend\App\Action\Context; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Controller; +use Magento\Framework\Validator\StringLength; +use Magento\MediaStorage\Model\File\Validator\AvailablePath; +use Magento\Sitemap\Model\SitemapFactory; -class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap +/** + * Save sitemap controller. + */ +class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap implements HttpPostActionInterface { /** * Maximum length of sitemap filename @@ -17,12 +24,12 @@ class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap const MAX_FILENAME_LENGTH = 32; /** - * @var \Magento\Framework\Validator\StringLength + * @var StringLength */ private $stringValidator; /** - * @var \Magento\MediaStorage\Model\File\Validator\AvailablePath + * @var AvailablePath */ private $pathValidator; @@ -37,33 +44,33 @@ class Save extends \Magento\Sitemap\Controller\Adminhtml\Sitemap private $filesystem; /** - * @var \Magento\Sitemap\Model\SitemapFactory + * @var SitemapFactory */ private $sitemapFactory; /** * Save constructor. - * @param Action\Context $context - * @param \Magento\Framework\Validator\StringLength $stringValidator - * @param \Magento\MediaStorage\Model\File\Validator\AvailablePath $pathValidator + * @param Context $context + * @param StringLength $stringValidator + * @param AvailablePath $pathValidator * @param \Magento\Sitemap\Helper\Data $sitemapHelper * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Sitemap\Model\SitemapFactory $sitemapFactory + * @param SitemapFactory $sitemapFactory */ public function __construct( - \Magento\Backend\App\Action\Context $context, - \Magento\Framework\Validator\StringLength $stringValidator = null, - \Magento\MediaStorage\Model\File\Validator\AvailablePath $pathValidator = null, + Context $context, + StringLength $stringValidator = null, + AvailablePath $pathValidator = null, \Magento\Sitemap\Helper\Data $sitemapHelper = null, \Magento\Framework\Filesystem $filesystem = null, - \Magento\Sitemap\Model\SitemapFactory $sitemapFactory = null + SitemapFactory $sitemapFactory = null ) { parent::__construct($context); - $this->stringValidator = $stringValidator ?: $this->_objectManager->get(\Magento\Framework\Validator\StringLength::class); - $this->pathValidator = $pathValidator ?: $this->_objectManager->get(\Magento\MediaStorage\Model\File\Validator\AvailablePath::class); + $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(\Magento\Sitemap\Model\SitemapFactory::class); + $this->sitemapFactory = $sitemapFactory ?: $this->_objectManager->get(SitemapFactory::class); } /** diff --git a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php index 70a8ab700d301..00f51b7e6c23f 100644 --- a/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php +++ b/app/code/Magento/Sitemap/Test/Unit/Controller/Adminhtml/Sitemap/SaveTest.php @@ -9,6 +9,9 @@ use Magento\Framework\Controller\ResultFactory; use Magento\Sitemap\Controller\Adminhtml\Sitemap\Save; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class SaveTest extends \PHPUnit\Framework\TestCase { /** From 6dd73f01058e0d029ba8538aae410cc7ce290a33 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 25 Feb 2019 16:00:48 +0200 Subject: [PATCH 201/254] Fix static tests. --- lib/web/mage/adminhtml/grid.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index e27dd9c2a0d26..5f7a709f04fea 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -530,17 +530,17 @@ define([ /** * @param {Object} event - * @param int lastId + * @param {*} lastId */ inputPage: function (event, lastId) { var element = Event.element(event), keyCode = event.keyCode || event.which, - enteredValue = parseInt(element.value), - lastId = parseInt(lastId); + enteredValue = parseInt(element.value, 10), + pageId = parseInt(lastId, 10); if (keyCode == Event.KEY_RETURN) { //eslint-disable-line eqeqeq - if (enteredValue > lastId) { - this.setPage(lastId); + if (enteredValue > pageId) { + this.setPage(pageId); } else { this.setPage(enteredValue); } From 94c176fa5cd1047187b1478945cbec663d283d62 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 25 Feb 2019 17:25:20 +0200 Subject: [PATCH 202/254] Fix static test. --- app/code/Magento/Sitemap/Block/Adminhtml/Edit/Form.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sitemap/Block/Adminhtml/Edit/Form.php b/app/code/Magento/Sitemap/Block/Adminhtml/Edit/Form.php index 2093e4d7d5736..91b6a8446894b 100644 --- a/app/code/Magento/Sitemap/Block/Adminhtml/Edit/Form.php +++ b/app/code/Magento/Sitemap/Block/Adminhtml/Edit/Form.php @@ -48,6 +48,8 @@ protected function _construct() } /** + * Configure form for sitemap. + * * @return $this */ protected function _prepareForm() From 0acf8c53d798370398af2f8b7b505d774682d240 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Mon, 25 Feb 2019 09:43:35 -0600 Subject: [PATCH 203/254] MQE-1460: Deliver weekly PR - Add migrated tag to MTF tests --- .../Cms/Test/TestCase/CreateCustomUrlRewriteEntityTest.xml | 4 ++-- .../VerifyConfigurableProductLayeredNavigationTest.xml | 1 + .../Test/TestCase/DeleteCustomerBackendEntityTest.xml | 1 + .../Customer/Test/TestCase/MassAssignCustomerGroupTest.xml | 2 ++ .../Test/TestCase/RegisterCustomerFrontendEntityTest.xml | 3 ++- .../Test/TestCase/UpdateCustomerFrontendEntityTest.xml | 3 +++ .../Test/TestCase/CreateCategoryRewriteEntityTest.xml | 4 +++- .../Test/TestCase/CreateCustomUrlRewriteEntityTest.xml | 2 ++ 8 files changed, 16 insertions(+), 4 deletions(-) diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCustomUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCustomUrlRewriteEntityTest.xml index 3682fce4267c6..8adbc1ae46c7b 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCustomUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CreateCustomUrlRewriteEntityTest.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\UrlRewrite\Test\TestCase\CreateCustomUrlRewriteEntityTest" summary="Create Custom URL Rewrites" ticketId="MAGETWO-25474"> <variation name="CreateCustomUrlRewriteEntityTestVariation3"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="urlRewrite/data/entity_type" xsi:type="string">Custom</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/target_path/entity" xsi:type="string">cms/page/view/page_id/%cmsPage::default%</data> @@ -19,7 +19,7 @@ <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteCustomRedirect" /> </variation> <variation name="CreateCustomUrlRewriteEntityTestVariation4"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="urlRewrite/data/entity_type" xsi:type="string">Custom</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/target_path/entity" xsi:type="string">cms/page/view/page_id/%cmsPage::default%</data> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/VerifyConfigurableProductLayeredNavigationTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/VerifyConfigurableProductLayeredNavigationTest.xml index 082b93ba62e03..9108b44a0e85b 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/VerifyConfigurableProductLayeredNavigationTest.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/VerifyConfigurableProductLayeredNavigationTest.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\ConfigurableProduct\Test\TestCase\VerifyConfigurableProductLayeredNavigationTest" summary="Verify OOS option configurable product in Layered Navigation on storefront"> <variation name="VerifyConfigurableProductLayeredNavigationTestVariation1" summary="Verify the out of stock configurable attribute option doesn't show in Layered navigation" ticketId="MAGETWO-89745"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="product" xsi:type="string">configurableProduct::product_with_3_sizes</data> <data name="productUpdate/childProductUpdate" xsi:type="array"> <item name="data" xsi:type="array"> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteCustomerBackendEntityTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteCustomerBackendEntityTest.xml index 77b63fe39d4a1..3807360e89ec0 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteCustomerBackendEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/DeleteCustomerBackendEntityTest.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\Customer\Test\TestCase\DeleteCustomerBackendEntityTest" summary="Delete Customer Backend Entity" ticketId="MAGETWO-24764"> <variation name="DeleteCustomerBackendEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customer/dataset" xsi:type="string">default</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerSuccessDeleteMessage" /> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerNotInGrid" /> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.xml index 4086a8585c8a8..c868d3332a5a9 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/MassAssignCustomerGroupTest.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\Customer\Test\TestCase\MassAssignCustomerGroupTest" summary="Mass Assign Customer's Group to Customers" ticketId="MAGETWO-27892"> <variation name="MassAssignCustomerGroupTestVariation1" summary="Customer is created and mass action for changing customer group to created group is applied"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customers" xsi:type="array"> <item name="0" xsi:type="string">default</item> </data> @@ -16,6 +17,7 @@ <constraint name="Magento\Customer\Test\Constraint\AssertCustomerGroupInGrid" /> </variation> <variation name="MassAssignCustomerGroupTestVariation2" summary="Two customers are created and mass actions for changing customer group to 'Retail' is applied" ticketId="MAGETWO-19456"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customers" xsi:type="array"> <item name="0" xsi:type="string">default</item> <item name="1" xsi:type="string">customer_US</item> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml index c1cf533583114..86c8e56d232d1 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/RegisterCustomerFrontendEntityTest.xml @@ -20,6 +20,7 @@ <constraint name="Magento\Customer\Test\Constraint\AssertCustomerLogout" /> </variation> <variation name="RegisterCustomerFrontendEntityTestVariation2" summary="Register new customer with subscribing"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customer/data/firstname" xsi:type="string">john</data> <data name="customer/data/lastname" xsi:type="string">doe</data> <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data> @@ -32,7 +33,7 @@ <constraint name="Magento\Newsletter\Test\Constraint\AssertCustomerIsSubscribedToNewsletter" /> </variation> <variation name="RegisterCustomerFrontendEntityTestVariation3" summary="Register Customer" ticketId="MAGETWO-12394"> - <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="customer/data/firstname" xsi:type="string">john</data> <data name="customer/data/lastname" xsi:type="string">doe</data> <data name="customer/data/email" xsi:type="string">johndoe%isolation%@example.com</data> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/UpdateCustomerFrontendEntityTest.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/UpdateCustomerFrontendEntityTest.xml index 2cbb9a0315e16..b96d688b0c4b6 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/UpdateCustomerFrontendEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/UpdateCustomerFrontendEntityTest.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\Customer\Test\TestCase\UpdateCustomerFrontendEntityTest" summary="Update Customer on Frontend" ticketId="MAGETWO-25925"> <variation name="UpdateCustomerFrontendEntityTestVariation1" summary="No XSS injection on update customer information add address" ticketId="MAGETWO-47189"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customer/data/firstname" xsi:type="string">Patrick</title></head><svg/onload=alert('XSS')></data> <data name="customer/data/lastname" xsi:type="string"><script>alert('Last name')</script></data> <data name="customer/data/current_password" xsi:type="string">123123^q</data> @@ -25,6 +26,7 @@ <constraint name="Magento\Customer\Test\Constraint\AssertCustomerForm" /> </variation> <variation name="UpdateCustomerFrontendEntityTestVariation2" summary="Update customer information and add UK address, assert customer name and address on address book tab"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customer/data/firstname" xsi:type="string">Jonny %isolation%</data> <data name="customer/data/lastname" xsi:type="string">Doe %isolation%</data> <data name="customer/data/email" xsi:type="string">jonny%isolation%@example.com</data> @@ -44,6 +46,7 @@ <constraint name="Magento\Customer\Test\Constraint\AssertCustomerNameFrontend" /> </variation> <variation name="UpdateCustomerFrontendEntityTestVariation3" summary="Update customer information and add France address"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="customer/data/firstname" xsi:type="string">Jean %isolation%</data> <data name="customer/data/lastname" xsi:type="string">Reno %isolation%</data> <data name="customer/data/email" xsi:type="string">jean%isolation%@example.com</data> 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 890b1d55a6475..1293c6e89b69a 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 @@ -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\UrlRewrite\Test\TestCase\CreateCategoryRewriteEntityTest" summary="Create Category URL Rewrites" ticketId="MAGETWO-24280"> <variation name="CreateCategoryRewriteEntityTestVariation1" summary="Add Permanent Redirect for Category" ticketId="MAGETWO-12407"> - <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="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">cat%isolation%-redirect.html</data> @@ -18,6 +18,7 @@ <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteCategoryRedirect" /> </variation> <variation name="CreateCategoryRewriteEntityTestVariation2" summary="Create Category URL Rewrites with no 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">test_request%isolation%</data> @@ -27,6 +28,7 @@ <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteInGrid" /> </variation> <variation name="CreateCategoryRewriteEntityTestVariation3" summary="Create Category URL Rewrites with Temporary 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/CreateCustomUrlRewriteEntityTest.xml b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCustomUrlRewriteEntityTest.xml index 159663ad391e2..d6c37b851aa12 100644 --- a/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCustomUrlRewriteEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/UrlRewrite/Test/TestCase/CreateCustomUrlRewriteEntityTest.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\CreateCustomUrlRewriteEntityTest" summary="Create Custom URL Rewrites" ticketId="MAGETWO-25474"> <variation name="CreateCustomUrlRewriteEntityTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="urlRewrite/data/entity_type" xsi:type="string">Custom</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/target_path/entity" xsi:type="string">catalog/category/view/id/%category::default_subcategory%</data> @@ -19,6 +20,7 @@ <constraint name="Magento\UrlRewrite\Test\Constraint\AssertUrlRewriteCustomRedirect" /> </variation> <variation name="CreateCustomUrlRewriteEntityTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="urlRewrite/data/entity_type" xsi:type="string">Custom</data> <data name="urlRewrite/data/store_id" xsi:type="string">Main Website/Main Website Store/Default Store View</data> <data name="urlRewrite/data/target_path/entity" xsi:type="string">catalog/product/view/id/%catalogProductSimple::default%</data> From 79b324f2087c32978d887d18a2df2c4e6912b2fb Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Mon, 25 Feb 2019 21:00:10 +0200 Subject: [PATCH 204/254] Test coverage of Adding configurable product to shopping cart #381 --- .../AddConfigurableProductToCartTest.php | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php new file mode 100644 index 0000000000000..88d682382c5c6 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php @@ -0,0 +1,153 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; + +class AddConfigurableProductToCartTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var Quote + */ + private $quote; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quote = $objectManager->create(Quote::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products.php + */ + public function testAddConfigurableProductToCart() + { + $variantSku = 'simple_41'; + $qty = 2; + + $maskedQuoteId = $this->getMaskedQuoteId(); + + $query = $this->getAddConfigurableProductMutationQuery($maskedQuoteId, $variantSku, $qty); + + $response = $this->graphQlQuery($query); + $cartItems = $response['addConfigurableProductsToCart']['cart']['items']; + self::assertEquals($qty, $cartItems[0]['qty']); + self::assertEquals($variantSku, $cartItems[0]['product']['sku']); + } + + + /** + * @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products.php + * @expectedException \Exception + * @expectedExceptionMessage The requested qty is not available + */ + public function testAddProductIfQuantityIsNotAvailable() + { + $variantSku = 'simple_41'; + $qty = 200; + + $maskedQuoteId = $this->getMaskedQuoteId(); + $query = $this->getAddConfigurableProductMutationQuery($maskedQuoteId, $variantSku, $qty); + + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Framework/Search/_files/product_configurable.php + * @expectedException \Exception + * @expectedExceptionMessage Product that you are trying to add is not available. + */ + public function testAddOutOfStockProduct() + { + $variantSku = 'simple_1010'; + $qty = 1; + $maskedQuoteId = $this->getMaskedQuoteId(); + $query = $this->getAddConfigurableProductMutationQuery($maskedQuoteId, $variantSku, $qty); + + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getMaskedQuoteId() + { + $this->quoteResource->load( + $this->quote, + 'test_order_1', + 'reserved_order_id' + ); + return $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + } + + /** + * @param string $maskedQuoteId + * @param string $sku + * @param int $qty + * + * @return string + */ + private function getAddConfigurableProductMutationQuery(string $maskedQuoteId, string $variantSku, int $qty) : string + { + return <<<QUERY +mutation { + addConfigurableProductsToCart( + input: { + cart_id: "{$maskedQuoteId}" + cartItems: [ + { + variant_sku: "{$variantSku}" + data: { + qty: {$qty} + sku: "{$variantSku}" + } + } + ] + } + ) { + cart { + items { + id + qty + product { + name + sku + } + ... on ConfigurableCartItem { + configurable_options { + option_label + } + } + } + } + } +} +QUERY; + } +} From acdb0ae0fff3a1f5088d2898e9ab7126a0d408c6 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Tue, 26 Feb 2019 10:29:57 +0200 Subject: [PATCH 205/254] Test coverage of Adding configurable product to shopping cart #381 --- .../Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php index 88d682382c5c6..02ce5d8495fa6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php @@ -43,6 +43,7 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php */ public function testAddConfigurableProductToCart() { @@ -62,6 +63,7 @@ public function testAddConfigurableProductToCart() /** * @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php * @expectedException \Exception * @expectedExceptionMessage The requested qty is not available */ @@ -78,6 +80,7 @@ public function testAddProductIfQuantityIsNotAvailable() /** * @magentoApiDataFixture Magento/Framework/Search/_files/product_configurable.php + * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php * @expectedException \Exception * @expectedExceptionMessage Product that you are trying to add is not available. */ From 70f25998f47337656e61300df1c348534f0d2271 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Tue, 26 Feb 2019 11:57:48 +0200 Subject: [PATCH 206/254] Fix static tests. --- .../Pricing/Price/MinimalTierPriceCalculator.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php b/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php index af81e67e86fce..a5e573caa381e 100644 --- a/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php +++ b/app/code/Magento/Catalog/Pricing/Price/MinimalTierPriceCalculator.php @@ -29,8 +29,10 @@ public function __construct(CalculatorInterface $calculator) } /** - * Get raw value of "as low as" as a minimal among tier prices - * {@inheritdoc} + * Get raw value of "as low as" as a minimal among tier prices{@inheritdoc} + * + * @param SaleableInterface $saleableItem + * @return float|null */ public function getValue(SaleableInterface $saleableItem) { @@ -49,8 +51,10 @@ public function getValue(SaleableInterface $saleableItem) } /** - * Return calculated amount object that keeps "as low as" value - * {@inheritdoc} + * Return calculated amount object that keeps "as low as" value{@inheritdoc} + * + * @param SaleableInterface $saleableItem + * @return AmountInterface|null */ public function getAmount(SaleableInterface $saleableItem) { From 0ae61466690bf19d4f505485159e1d009aed91a9 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Tue, 26 Feb 2019 12:14:07 +0200 Subject: [PATCH 207/254] Notice: Undefined index: shipping_carrier_code is returned instead of Required parameter "shipping_carrier_code" is missing --- .../Model/Resolver/SetShippingMethodsOnCart.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php index 920829f5d67b1..639e724113615 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php @@ -69,13 +69,13 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $shippingMethod = reset($shippingMethods); // This point can be extended for multishipping - if (!$shippingMethod['cart_address_id']) { + if (!array_key_exists('cart_address_id', $shippingMethod)) { throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); } - if (!$shippingMethod['shipping_carrier_code']) { + if (!array_key_exists('shipping_carrier_code', $shippingMethod)) { throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - if (!$shippingMethod['shipping_method_code']) { + if (!array_key_exists('shipping_method_code', $shippingMethod)) { throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); } From f14ffca4a66a67a6af31e6491b1e2debb3419986 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 26 Feb 2019 10:52:38 -0600 Subject: [PATCH 208/254] MAGETWO-98357: The required product attribute is not displayed when change attribute set --- .../Controller/Adminhtml/Product/Builder.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php index 125406061aed7..5c87ee7c9fcac 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Controller\Adminhtml\Product; use Magento\Catalog\Model\ProductFactory; @@ -15,6 +17,11 @@ use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Type as ProductTypes; +/** + * Build a product based on a request + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class Builder { /** @@ -81,8 +88,9 @@ public function __construct( * @param RequestInterface $request * @return \Magento\Catalog\Model\Product * @throws \RuntimeException + * @throws \Magento\Framework\Exception\LocalizedException */ - public function build(RequestInterface $request) + public function build(RequestInterface $request): Product { $productId = (int) $request->getParam('id'); $storeId = $request->getParam('store', 0); @@ -92,6 +100,7 @@ public function build(RequestInterface $request) if ($productId) { try { $product = $this->productRepository->getById($productId, true, $storeId); + $product->setAttributeSetId($attributeSetId); } catch (\Exception $e) { $product = $this->createEmptyProduct(ProductTypes::DEFAULT_TYPE, $attributeSetId, $storeId); $this->logger->critical($e); @@ -113,6 +122,8 @@ public function build(RequestInterface $request) } /** + * Create a product with the given properties + * * @param int $typeId * @param int $attributeSetId * @param int $storeId From 495c540c55b760a61746835139c14ab51f843c51 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 26 Feb 2019 12:18:47 -0600 Subject: [PATCH 209/254] GraphQL-293: [Payment methods] Set Payment Method on Cart --- .../QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php | 2 +- .../QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php | 5 +---- app/code/Magento/QuoteGraphQl/etc/schema.graphqls | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php index 5d6aed15821f3..7a99b04638ac3 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php @@ -36,7 +36,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return [ 'code' => $payment->getMethod(), - 'po_number' => $payment->getPoNumber(), + 'purchase_order_number' => $payment->getPoNumber(), ]; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php index 82709de03b9b6..78a841a9cb614 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php @@ -81,10 +81,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new GraphQlInputException(__('Required parameter payment "code" is missing')); } - $poNumber = $this->arrayManager->get('input/payment_method/po_number', $args); - if (!$poNumber) { - throw new GraphQlInputException(__('Required parameter payment "po_number" is missing')); - } + $poNumber = $this->arrayManager->get('input/payment_method/purchase_order_number', $args); $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); $payment = $this->paymentFactory->create([ diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 78c12f939d8fb..41d67020fcf29 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -110,7 +110,7 @@ input SetPaymentMethodOnCartInput { input PaymentMethodInput { code: String! @doc(description:"Payment method code") - po_number: String! @doc(description:"Purchase order number") + purchase_order_number: String @doc(description:"Purchase order number") additional_data: PaymentMethodAdditionalDataInput } @@ -203,7 +203,7 @@ type AvailablePaymentMethod { type SelectedPaymentMethod { code: String @doc(description: "The payment method code") - po_number: String @doc(description: "The purchase order number.") + purchase_order_number: String @doc(description: "The purchase order number.") additional_data: SelectedPaymentMethodAdditionalData } From 86857b3d9ad17fd4ffcd2918951e92a1d3188be8 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Tue, 26 Feb 2019 20:18:57 +0200 Subject: [PATCH 210/254] Notice: Undefined index: shipping_carrier_code is returned instead of Required parameter "shipping_carrier_code" is missin #405 --- .../Model/Resolver/SetShippingMethodsOnCart.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php index 639e724113615..47ea94a173053 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php @@ -69,13 +69,13 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $shippingMethod = reset($shippingMethods); // This point can be extended for multishipping - if (!array_key_exists('cart_address_id', $shippingMethod)) { + if (!isset($shippingMethod['cart_address_id'])) { throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); } - if (!array_key_exists('shipping_carrier_code', $shippingMethod)) { + if (!isset($shippingMethod['shipping_carrier_code'])) { throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - if (!array_key_exists('shipping_method_code', $shippingMethod)) { + if (!isset($shippingMethod['shipping_method_code'])) { throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); } From 287186d61baddc38a64c140e0ee3b20a404e7aaf Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 26 Feb 2019 12:44:27 -0600 Subject: [PATCH 211/254] GraphQL-293: [Payment methods] Set Payment Method on Cart --- .../Quote/SetPaymentMethodOnCartTest.php | 245 +++--------------- 1 file changed, 36 insertions(+), 209 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php index 496c3efbbd898..89e88bd0ce04a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php @@ -7,33 +7,19 @@ namespace Magento\GraphQl\Quote; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\OfflinePayments\Model\Banktransfer; -use Magento\OfflinePayments\Model\Cashondelivery; use Magento\OfflinePayments\Model\Checkmo; -use Magento\OfflinePayments\Model\Purchaseorder; -use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; -use Magento\Framework\App\Cache\TypeListInterface; -use Magento\Config\Model\ResourceModel\Config; /** * Test for setting payment methods on cart */ class SetPaymentMethodOnCartTest extends GraphQlAbstract { - private const OFFLINE_METHOD_CODES = [ - Checkmo::PAYMENT_METHOD_CHECKMO_CODE, - Banktransfer::PAYMENT_METHOD_BANKTRANSFER_CODE, - Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE, - Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE, - ]; - /** * @var CustomerTokenServiceInterface */ @@ -45,139 +31,56 @@ class SetPaymentMethodOnCartTest extends GraphQlAbstract private $quoteResource; /** - * @var Quote + * @var QuoteFactory */ - private $quote; + private $quoteFactory; /** * @var QuoteIdToMaskedQuoteIdInterface */ private $quoteIdToMaskedId; - /** - * @var Config - */ - private $config; - - /** - * @var TypeListInterface - */ - private $cacheList; - /** * @inheritdoc */ protected function setUp() { $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->create(QuoteResource::class); - $this->quote = $objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); - $this->config = $objectManager->get(Config::class); - $this->cacheList = $objectManager->get(TypeListInterface::class); - - foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { - $this->config->saveConfig( - 'payment/' . $offlineMethodCode . '/active', - '1', - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - } - $this->cacheList->cleanType('config'); } /** - * @inheritdoc - */ - protected function tearDown() - { - foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { - //Never no disable checkmo method - if ($offlineMethodCode === Checkmo::PAYMENT_METHOD_CHECKMO_CODE) { - continue; - } - $this->config->saveConfig( - 'payment/' . $offlineMethodCode . '/active', - '0', - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - } - $this->cacheList->cleanType('config'); - } - - /** - * @param string $methodCode - * @dataProvider dataProviderOfflinePaymentMethods * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php */ - public function testSetPaymentMethodOnCart(string $methodCode) + public function testSetPaymentMethodOnCart() { - /** @var \Magento\Config\Model\ResourceModel\Config $config */ - $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); - $config->saveConfig( - 'payment/' . $methodCode . '/active', - 1, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $methodCode - ); + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $response = $this->sendRequestWithToken($query); - $this->assertArrayHasKey('setPaymentMethodOnCart', $response); - $this->assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - $this->assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); - $this->assertArrayHasKey('payment_method', $response['setPaymentMethodOnCart']['cart']); - $this->assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['payment_method']['code']); - } - - public function dataProviderOfflinePaymentMethods(): array - { - $methods = []; - foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { - //Purchase order requires additional input and is tested separately - if ($offlineMethodCode === Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE) { - continue; - } - $methods[] = [$offlineMethodCode]; - } - - return $methods; + self::assertArrayHasKey('setPaymentMethodOnCart', $response); + self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); + self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); + self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); + self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The requested Payment Method is not available. */ public function testSetNonExistingPaymentMethod() { - $paymentMethod = 'noway'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $paymentMethod - ); + $methodCode = 'noway'; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); - $this->expectExceptionMessage('The requested Payment Method is not available.'); + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $this->sendRequestWithToken($query); } @@ -186,18 +89,10 @@ public function testSetNonExistingPaymentMethod() */ public function testSetPaymentMethodByGuestToCustomerCart() { - $paymentMethod = 'checkmo'; - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); + $methodCode = 'checkmo'; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $paymentMethod - ); + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" @@ -206,84 +101,6 @@ public function testSetPaymentMethodByGuestToCustomerCart() $this->graphQlQuery($query); } - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetPaymentMethodPurchaseOrderOnCart() - { - $methodCode = \Magento\OfflinePayments\Model\Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE; - $poNumber = 'GQL-19002'; - - /** @var \Magento\Config\Model\ResourceModel\Config $config */ - $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); - $config->saveConfig( - 'payment/' . $methodCode . '/active', - 1, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = <<<QUERY -mutation { - setPaymentMethodOnCart(input: - { - cart_id: "$maskedQuoteId", - payment_method: { - code: "$methodCode" - po_number: "$poNumber" - } - }) { - - cart { - cart_id, - payment_method { - code - po_number - } - } - } -} - -QUERY; - - $response = $this->sendRequestWithToken($query); - - $this->assertArrayHasKey('setPaymentMethodOnCart', $response); - $this->assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - $this->assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); - $this->assertArrayHasKey('payment_method', $response['setPaymentMethodOnCart']['cart']); - $this->assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['payment_method']['code']); - $this->assertEquals($poNumber, $response['setPaymentMethodOnCart']['cart']['payment_method']['po_number']); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testPurchaseOrderPaymentMethodFailingValidation() - { - $this->quoteResource->load( - $this->quote, - 'test_order_1', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE - ); - - $this->expectExceptionMessage('Purchase order number is a required field.'); - $this->sendRequestWithToken($query); - } - /** * Generates query for setting the specified shipping method on cart * @@ -307,13 +124,12 @@ private function prepareMutationQuery( cart { cart_id, - payment_method { + selected_payment_method { code } } } } - QUERY; } @@ -326,10 +142,21 @@ private function prepareMutationQuery( */ private function sendRequestWithToken(string $query): array { - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; return $this->graphQlQuery($query, [], '', $headerMap); } + + /** + * @param string $reversedQuoteId + * @return string + */ + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } } From bcf7d9c0ef8cbd514774c24017e3d83a60353538 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 26 Feb 2019 13:11:04 -0600 Subject: [PATCH 212/254] MAGETWO-98357: The required product attribute is not displayed when change attribute set --- .../Catalog/Controller/Adminhtml/Product/Builder.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php index 5c87ee7c9fcac..78ad9f423871f 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Builder.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Controller\Adminhtml\Product; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ProductFactory; use Magento\Cms\Model\Wysiwyg as WysiwygModel; use Magento\Framework\App\RequestInterface; @@ -86,11 +87,11 @@ public function __construct( * Build product based on user request * * @param RequestInterface $request - * @return \Magento\Catalog\Model\Product + * @return ProductInterface * @throws \RuntimeException * @throws \Magento\Framework\Exception\LocalizedException */ - public function build(RequestInterface $request): Product + public function build(RequestInterface $request): ProductInterface { $productId = (int) $request->getParam('id'); $storeId = $request->getParam('store', 0); @@ -100,7 +101,9 @@ public function build(RequestInterface $request): Product if ($productId) { try { $product = $this->productRepository->getById($productId, true, $storeId); - $product->setAttributeSetId($attributeSetId); + if ($attributeSetId) { + $product->setAttributeSetId($attributeSetId); + } } catch (\Exception $e) { $product = $this->createEmptyProduct(ProductTypes::DEFAULT_TYPE, $attributeSetId, $storeId); $this->logger->critical($e); From e6d7b13a678014faebac0fac0de805d43618a9d5 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 26 Feb 2019 15:56:29 -0600 Subject: [PATCH 213/254] MAGETWO-98357: The required product attribute is not displayed when change attribute set --- .../Test/AdminChangeProductAttributeSet.xml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml new file mode 100644 index 0000000000000..73280249eb68d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.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="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="AssignAttributeToGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="$$createProductAttribute.attribute_code$$"/> + </actionGroup> + <actionGroup ref="SaveAttributeSet" stepKey="SaveAttributeSet"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> + </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> From 4cae1af66482f7f67211e3e87d47456f1db3088a Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 26 Feb 2019 17:43:46 -0600 Subject: [PATCH 214/254] GraphQL-293: [Payment methods] Set Payment Method on Cart --- .../Model/Cart/GetCartForUser.php | 10 +- .../SetPaymentMethodOnCartTest.php | 315 ++++++++++++++++++ .../GraphQl/Quote/CreateEmptyCartTest.php | 91 ----- .../Quote/Customer/CreateEmptyCartTest.php | 61 ++++ .../GetAvailablePaymentMethodsTest.php | 102 ++++++ .../Quote/{ => Customer}/GetCartTest.php | 58 ++-- .../SetBillingAddressOnCartTest.php | 177 ++++------ .../Customer/SetPaymentMethodOnCartTest.php | 227 +++++++++++++ .../Quote/GetAvailablePaymentMethodsTest.php | 152 --------- .../Quote/Guest/CreateEmptyCartTest.php | 47 +++ .../Guest/GetAvailablePaymentMethodsTest.php | 90 +++++ .../GraphQl/Quote/Guest/GetCartTest.php | 151 +++++++++ .../Guest/SetBillingAddressOnCartTest.php | 263 +++++++++++++++ .../SetPaymentMethodOnCartTest.php | 83 +++-- .../Quote/SetShippingAddressOnCartTest.php | 2 +- 15 files changed, 1423 insertions(+), 406 deletions(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/OfflinePayments/SetPaymentMethodOnCartTest.php delete mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CreateEmptyCartTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php rename dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/{ => Customer}/GetCartTest.php (70%) rename dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/{ => Customer}/SetBillingAddressOnCartTest.php (82%) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php delete mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailablePaymentMethodsTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php rename dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/{ => Guest}/SetPaymentMethodOnCartTest.php (59%) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php index c3207bf478bbe..21df2271cc7f3 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php @@ -45,12 +45,12 @@ public function __construct( * Get cart for user * * @param string $cartHash - * @param int|null $userId + * @param int|null $customerId * @return Quote * @throws GraphQlAuthorizationException * @throws GraphQlNoSuchEntityException */ - public function execute(string $cartHash, ?int $userId): Quote + public function execute(string $cartHash, ?int $customerId): Quote { try { $cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash); @@ -69,14 +69,14 @@ public function execute(string $cartHash, ?int $userId): Quote ); } - $customerId = (int)$cart->getCustomerId(); + $cartCustomerId = (int)$cart->getCustomerId(); /* Guest cart, allow operations */ - if (!$customerId) { + if (!$cartCustomerId && null === $customerId) { return $cart; } - if ($customerId !== $userId) { + if ($cartCustomerId !== $customerId) { throw new GraphQlAuthorizationException( __( 'The current user cannot perform operations on cart "%masked_cart_id"', diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflinePayments/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflinePayments/SetPaymentMethodOnCartTest.php new file mode 100644 index 0000000000000..3a3e50efa0676 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflinePayments/SetPaymentMethodOnCartTest.php @@ -0,0 +1,315 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\OfflinePayments; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\OfflinePayments\Model\Banktransfer; +use Magento\OfflinePayments\Model\Cashondelivery; +use Magento\OfflinePayments\Model\Checkmo; +use Magento\OfflinePayments\Model\Purchaseorder; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Config\Model\ResourceModel\Config; + +/** + * Test for setting payment methods on cart + */ +class SetPaymentMethodOnCartTest extends GraphQlAbstract +{ + private const OFFLINE_METHOD_CODES = [ + Checkmo::PAYMENT_METHOD_CHECKMO_CODE, +// Banktransfer::PAYMENT_METHOD_BANKTRANSFER_CODE, +// Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE, +// Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE, + ]; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @var Config + */ + private $config; + + /** + * @var TypeListInterface + */ + private $cacheList; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->config = $objectManager->get(Config::class); + $this->cacheList = $objectManager->get(TypeListInterface::class); + + foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { + $this->config->saveConfig( + 'payment/' . $offlineMethodCode . '/active', + '1', + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + } + $this->cacheList->cleanType('config'); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { +// foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { +// //Never no disable checkmo method +// if ($offlineMethodCode === Checkmo::PAYMENT_METHOD_CHECKMO_CODE) { +// continue; +// } +// $this->config->saveConfig( +// 'payment/' . $offlineMethodCode . '/active', +// '0', +// ScopeConfigInterface::SCOPE_TYPE_DEFAULT, +// 0 +// ); +// } +// $this->cacheList->cleanType('config'); + } + + /** + * @param string $methodCode + * @dataProvider dataProviderOfflinePaymentMethods + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetPaymentMethodOnCart(string $methodCode) + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $methodCode + ); + + $response = $this->sendRequestWithToken($query); + + self::assertArrayHasKey('setPaymentMethodOnCart', $response); + self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); + self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); + self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); + self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); + } + + public function dataProviderOfflinePaymentMethods(): array + { + $methods = []; + foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { + //Purchase order requires additional input and is tested separately + if ($offlineMethodCode === Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE) { + continue; + } + $methods[] = [$offlineMethodCode]; + } + + return $methods; + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetNonExistingPaymentMethod() + { + $paymentMethod = 'noway'; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $paymentMethod + ); + + $this->expectExceptionMessage('The requested Payment Method is not available.'); + $this->sendRequestWithToken($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetPaymentMethodByGuestToCustomerCart() + { + $paymentMethod = 'checkmo'; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + $paymentMethod + ); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetPaymentMethodPurchaseOrderOnCart() + { + $methodCode = \Magento\OfflinePayments\Model\Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE; + $poNumber = 'GQL-19002'; + + /** @var \Magento\Config\Model\ResourceModel\Config $config */ + $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); + $config->saveConfig( + 'payment/' . $methodCode . '/active', + 1, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + + $query = <<<QUERY +mutation { + setPaymentMethodOnCart(input: + { + cart_id: "$maskedQuoteId", + payment_method: { + code: "$methodCode" + purchase_order_number: "$poNumber" + } + }) { + + cart { + cart_id, + selected_payment_method { + code + purchase_order_number + } + } + } +} + +QUERY; + + $response = $this->sendRequestWithToken($query); + + self::assertArrayHasKey('setPaymentMethodOnCart', $response); + self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); + self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); + self::assertArrayHasKey('payment_method', $response['setPaymentMethodOnCart']['cart']); + self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); + self::assertEquals( + $poNumber, + $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['purchase_order_number'] + ); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testPurchaseOrderPaymentMethodFailingValidation() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + + $query = $this->prepareMutationQuery( + $maskedQuoteId, + Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE + ); + + $this->expectExceptionMessage('Purchase order number is a required field.'); + $this->sendRequestWithToken($query); + } + + /** + * Generates query for setting the specified shipping method on cart + * + * @param string $maskedQuoteId + * @param string $methodCode + * @return string + */ + private function prepareMutationQuery( + string $maskedQuoteId, + string $methodCode + ) : string { + return <<<QUERY +mutation { + setPaymentMethodOnCart(input: + { + cart_id: "$maskedQuoteId", + payment_method: { + code: "$methodCode" + } + }) { + + cart { + cart_id, + selected_payment_method { + code + } + } + } +} + +QUERY; + } + + /** + * Sends a GraphQL request with using a bearer token + * + * @param string $query + * @return array + * @throws \Magento\Framework\Exception\AuthenticationException + */ + private function sendRequestWithToken(string $query): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + + return $this->graphQlQuery($query, [], '', $headerMap); + } + + /** + * @param string $reversedQuoteId + * @return string + */ + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CreateEmptyCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CreateEmptyCartTest.php deleted file mode 100644 index 6e819b523ec82..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CreateEmptyCartTest.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\GraphQl\Quote; - -use Magento\Quote\Api\Data\CartInterface; -use Magento\TestFramework\ObjectManager; -use Magento\TestFramework\TestCase\GraphQlAbstract; -use Magento\Quote\Model\QuoteIdMask; -use Magento\Quote\Api\GuestCartRepositoryInterface; - -/** - * Test for empty cart creation mutation - */ -class CreateEmptyCartTest extends GraphQlAbstract -{ - /** - * @var QuoteIdMask - */ - private $quoteIdMask; - - /** - * @var ObjectManager - */ - private $objectManager; - - /** - * @var GuestCartRepositoryInterface - */ - private $guestCartRepository; - - protected function setUp() - { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->quoteIdMask = $this->objectManager->create(QuoteIdMask::class); - $this->guestCartRepository = $this->objectManager->create(GuestCartRepositoryInterface::class); - } - - public function testCreateEmptyCartForGuest() - { - $query = <<<QUERY -mutation { - createEmptyCart -} -QUERY; - $response = $this->graphQlQuery($query); - - self::assertArrayHasKey('createEmptyCart', $response); - - $maskedCartId = $response['createEmptyCart']; - /** @var CartInterface $guestCart */ - $guestCart = $this->guestCartRepository->get($maskedCartId); - - self::assertNotNull($guestCart->getId()); - self::assertNull($guestCart->getCustomer()->getId()); - } - - /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php - */ - public function testCreateEmptyCartForRegisteredCustomer() - { - $query = <<<QUERY -mutation { - createEmptyCart -} -QUERY; - - /** @var \Magento\Integration\Api\CustomerTokenServiceInterface $customerTokenService */ - $customerTokenService = $this->objectManager->create( - \Magento\Integration\Api\CustomerTokenServiceInterface::class - ); - $customerToken = $customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - - $response = $this->graphQlQuery($query, [], '', $headerMap); - - self::assertArrayHasKey('createEmptyCart', $response); - - $maskedCartId = $response['createEmptyCart']; - /* guestCartRepository is used for registered customer to get the cart hash */ - $guestCart = $this->guestCartRepository->get($maskedCartId); - - self::assertNotNull($guestCart->getId()); - self::assertEquals(1, $guestCart->getCustomer()->getId()); - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php new file mode 100644 index 0000000000000..0cb8a38b0cb5e --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/CreateEmptyCartTest.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Customer; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Quote\Api\GuestCartRepositoryInterface; + +/** + * Test for empty cart creation mutation for customer + */ +class CreateEmptyCartTest extends GraphQlAbstract +{ + /** + * @var GuestCartRepositoryInterface + */ + private $guestCartRepository; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->guestCartRepository = $objectManager->get(GuestCartRepositoryInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testCreateEmptyCart() + { + $query = <<<QUERY +mutation { + createEmptyCart +} +QUERY; + + $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + + $response = $this->graphQlQuery($query, [], '', $headerMap); + + self::assertArrayHasKey('createEmptyCart', $response); + + $maskedCartId = $response['createEmptyCart']; + $guestCart = $this->guestCartRepository->get($maskedCartId); + + self::assertNotNull($guestCart->getId()); + self::assertEquals(1, $guestCart->getCustomer()->getId()); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php new file mode 100644 index 0000000000000..b5323ee986582 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Customer; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for get available payment methods + */ +class GetAvailablePaymentMethodsTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + */ + public function testGetCartWithPaymentMethods() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + + $query = <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + available_payment_methods { + code + title + } + } +} +QUERY; + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('cart', $response); + self::assertCount(1, $response['cart']['available_payment_methods']); + self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); + self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); + } + + /** + * @param string $username + * @param string $password + * @return array + */ + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } + + /** + * @param string $reversedQuoteId + * @return string + */ + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php similarity index 70% rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php index 6c5add7df6b0f..8c1fcce7fb550 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\GraphQl\Quote; +namespace Magento\GraphQl\Quote\Customer; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\Quote\Model\QuoteFactory; @@ -49,49 +49,62 @@ protected function setUp() } /** - * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php */ - public function testGetCartForGuest() + public function testGetCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); $query = $this->getCartQuery($maskedQuoteId); - $response = $this->graphQlQuery($query); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response); self::assertEquals($maskedQuoteId, $response['cart']['cart_id']); + + self::assertArrayHasKey('items', $response['cart']); + self::assertCount(2, $response['cart']['items']); + + self::assertNotEmpty($response['cart']['items'][0]['id']); + self::assertEquals($response['cart']['items'][0]['qty'], 2); + self::assertEquals($response['cart']['items'][0]['product']['sku'], 'simple'); + + self::assertNotEmpty($response['cart']['items'][1]['id']); + self::assertEquals($response['cart']['items'][1]['qty'], 1); + self::assertEquals($response['cart']['items'][1]['product']['sku'], 'simple_one'); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php */ - public function testGetCartByRegisteredCustomer() + public function testGetGuestCart() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = $this->getCartQuery($maskedQuoteId); - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - - self::assertArrayHasKey('cart', $response); - self::assertEquals($maskedQuoteId, $response['cart']['cart_id']); - self::assertNotEmpty($response['cart']['items']); + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" + ); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** + * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php */ - public function testGetCartOfAnotherCustomerByGuest() + public function testGetCartIfCustomerIsNotOwnerOfCart() { $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); $query = $this->getCartQuery($maskedQuoteId); - self:$this->expectExceptionMessage( + $this->expectExceptionMessage( "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" ); - $this->graphQlQuery($query); + $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); } /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException \Exception * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" */ @@ -100,7 +113,7 @@ public function testGetNonExistentCart() $maskedQuoteId = 'non_existent_masked_id'; $query = $this->getCartQuery($maskedQuoteId); - $this->graphQlQuery($query); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** @@ -114,15 +127,12 @@ private function getCartQuery( { cart(cart_id: "$maskedQuoteId") { cart_id - applied_coupon { - code - } items { id - } - shipping_addresses { - firstname, - lastname + qty + product { + sku + } } } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php similarity index 82% rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php index bfe57109d107d..d244fcb27f021 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetBillingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php @@ -5,17 +5,14 @@ */ declare(strict_types=1); -namespace Magento\GraphQl\Quote; +namespace Magento\GraphQl\Quote\Customer; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Multishipping\Helper\Data; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; -use Magento\TestFramework\ObjectManager; /** * Test for set billing address on cart mutation @@ -53,10 +50,11 @@ protected function setUp() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php */ - public function testSetNewBillingAddressByGuest() + public function testSetNewBillingAddress() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->assignQuoteToCustomer(); $query = <<<QUERY mutation { @@ -93,7 +91,7 @@ public function testSetNewBillingAddressByGuest() } } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -104,10 +102,11 @@ public function testSetNewBillingAddressByGuest() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Customer/_files/customer.php */ - public function testSetNewBillingAddressWithUseForShippingParameterByGuest() + public function testSetNewBillingAddressWithUseForShippingParameter() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->assignQuoteToCustomer(); $query = <<<QUERY mutation { @@ -154,7 +153,7 @@ public function testSetNewBillingAddressWithUseForShippingParameterByGuest() } } QUERY; - $response = $this->graphQlQuery($query); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); $cartResponse = $response['setBillingAddressOnCart']['cart']; @@ -168,12 +167,12 @@ public function testSetNewBillingAddressWithUseForShippingParameterByGuest() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @expectedException \Exception - * @expectedExceptionMessage The current customer isn't authorized. + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php */ - public function testSetBillingAddressFromAddressBookByGuest() + public function testSetBillingAddressFromAddressBook() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->assignQuoteToCustomer(); $query = <<<QUERY mutation { @@ -182,25 +181,39 @@ public function testSetBillingAddressFromAddressBookByGuest() cart_id: "$maskedQuoteId" billing_address: { customer_address_id: 1 - } + } } ) { cart { billing_address { + firstname + lastname + company + street city + postcode + telephone } } } } QUERY; - $this->graphQlQuery($query); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + $cartResponse = $response['setBillingAddressOnCart']['cart']; + self::assertArrayHasKey('billing_address', $cartResponse); + $billingAddressResponse = $cartResponse['billing_address']; + $this->assertSavedBillingAddressFields($billingAddressResponse); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage Could not find a address with ID "100" */ - public function testSetNewBillingAddressByRegisteredCustomer() + public function testSetNotExistedBillingAddressFromAddressBook() { $maskedQuoteId = $this->assignQuoteToCustomer(); @@ -210,42 +223,19 @@ public function testSetNewBillingAddressByRegisteredCustomer() input: { cart_id: "$maskedQuoteId" billing_address: { - address: { - firstname: "test firstname" - lastname: "test lastname" - company: "test company" - street: ["test street 1", "test street 2"] - city: "test city" - region: "test region" - postcode: "887766" - country_code: "US" - telephone: "88776655" - save_in_address_book: false - } - } + customer_address_id: 100 + } } ) { cart { billing_address { - firstname - lastname - company - street city - postcode - telephone } } } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - - self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); - $cartResponse = $response['setBillingAddressOnCart']['cart']; - self::assertArrayHasKey('billing_address', $cartResponse); - $billingAddressResponse = $cartResponse['billing_address']; - $this->assertNewAddressFields($billingAddressResponse); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** @@ -253,7 +243,7 @@ public function testSetNewBillingAddressByRegisteredCustomer() * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php */ - public function testSetBillingAddressFromAddressBookByRegisteredCustomer() + public function testSetNewBillingAddressAndFromAddressBookAtSameTime() { $maskedQuoteId = $this->assignQuoteToCustomer(); @@ -263,42 +253,45 @@ public function testSetBillingAddressFromAddressBookByRegisteredCustomer() input: { cart_id: "$maskedQuoteId" billing_address: { - customer_address_id: 1 - } + customer_address_id: 1 + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } } ) { cart { billing_address { - firstname - lastname - company - street city - postcode - telephone } } } } QUERY; - $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); - self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); - $cartResponse = $response['setBillingAddressOnCart']['cart']; - self::assertArrayHasKey('billing_address', $cartResponse); - $billingAddressResponse = $cartResponse['billing_address']; - $this->assertSavedBillingAddressFields($billingAddressResponse); + self::expectExceptionMessage( + 'The billing address cannot contain "customer_address_id" and "address" at the same time.' + ); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @expectedException \Exception - * @expectedExceptionMessage Could not find a address with ID "100" + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ - public function testSetNotExistedBillingAddressFromAddressBook() + public function testSetBillingAddressToGuestCart() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); $query = <<<QUERY mutation { @@ -306,7 +299,7 @@ public function testSetNotExistedBillingAddressFromAddressBook() input: { cart_id: "$maskedQuoteId" billing_address: { - customer_address_id: 100 + customer_address_id: 1 } } ) { @@ -318,17 +311,21 @@ public function testSetNotExistedBillingAddressFromAddressBook() } } QUERY; + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" + ); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); } /** + * @magentoApiDataFixture Magento/Customer/_files/three_customers.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php */ - public function testSetNewBillingAddressAndFromAddressBookAtSameTime() + public function testSetBillingAddressIfCustomerIsNotOwnerOfCart() { - $maskedQuoteId = $this->assignQuoteToCustomer(); + $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 2); $query = <<<QUERY mutation { @@ -336,20 +333,8 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() input: { cart_id: "$maskedQuoteId" billing_address: { - customer_address_id: 1 - address: { - firstname: "test firstname" - lastname: "test lastname" - company: "test company" - street: ["test street 1", "test street 2"] - city: "test city" - region: "test region" - postcode: "887766" - country_code: "US" - telephone: "88776655" - save_in_address_book: false - } - } + customer_address_id: 1 + } } ) { cart { @@ -360,11 +345,11 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() } } QUERY; - - self::expectExceptionMessage( - 'The billing address cannot contain "customer_address_id" and "address" at the same time.' + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" ); - $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer@search.example.com')); } /** @@ -376,7 +361,7 @@ public function testSetNewBillingAddressAndFromAddressBookAtSameTime() */ public function testSetBillingAddressIfCustomerIsNotOwnerOfAddress() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 2); $query = <<<QUERY mutation { @@ -478,22 +463,4 @@ private function assignQuoteToCustomer( $this->quoteResource->save($quote); return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } - - public function tearDown() - { - /** @var \Magento\Config\Model\ResourceModel\Config $config */ - $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); - - //default state of multishipping config - $config->saveConfig( - Data::XML_PATH_CHECKOUT_MULTIPLE_AVAILABLE, - 1, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - - /** @var \Magento\Framework\App\Config\ReinitableConfigInterface $config */ - $config = ObjectManager::getInstance()->get(\Magento\Framework\App\Config\ReinitableConfigInterface::class); - $config->reinit(); - } } 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 new file mode 100644 index 0000000000000..8856b2ab44c22 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php @@ -0,0 +1,227 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Customer; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\OfflinePayments\Model\Checkmo; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for setting payment methods on cart by customer + */ +class SetPaymentMethodOnCartTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + */ + public function testSetPaymentWithVirtualProduct() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_virtual_product'); + + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('setPaymentMethodOnCart', $response); + self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); + self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); + self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); + self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetPaymentWithSimpleProduct() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('setPaymentMethodOnCart', $response); + self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); + self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); + self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); + self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The shipping address is missing. Set the address and try again. + */ + public function testSetPaymentWithSimpleProductWithoutAddress() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 1); + + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The requested Payment Method is not available. + */ + public function testSetNonExistingPaymentMethod() + { + $methodCode = 'noway'; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testSetPaymentMethodToGuestCart() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/three_customers.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSetPaymentMethodIfCustomerIsNotOwnerOfCart() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + + $this->graphQlQuery($query, [], '', $this->getHeaderMap('customer2@search.example.com')); + } + + /** + * Generates query for setting the specified shipping method on cart + * + * @param string $maskedQuoteId + * @param string $methodCode + * @return string + */ + private function prepareMutationQuery( + string $maskedQuoteId, + string $methodCode + ) : string { + return <<<QUERY +mutation { + setPaymentMethodOnCart(input: + { + cart_id: "$maskedQuoteId", + payment_method: { + code: "$methodCode" + } + }) { + + cart { + cart_id, + selected_payment_method { + code + } + } + } +} +QUERY; + } + + /** + * @param string $reversedQuoteId + * @return string + */ + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } + + /** + * @param string $reversedQuoteId + * @param int $customerId + * @return string + */ + private function assignQuoteToCustomer( + string $reversedQuoteId, + int $customerId + ): string { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $quote->setCustomerId($customerId); + $this->quoteResource->save($quote); + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } + + /** + * @param string $username + * @param string $password + * @return array + */ + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailablePaymentMethodsTest.php deleted file mode 100644 index d6eff6816b342..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailablePaymentMethodsTest.php +++ /dev/null @@ -1,152 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\Quote; - -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Quote\Model\Quote; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\GraphQlAbstract; - -/** - * Test for getting cart information - */ -class GetAvailablePaymentMethodsTest extends GraphQlAbstract -{ - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var Quote - */ - private $quote; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->create(QuoteResource::class); - $this->quote = $objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php - */ - public function testGetCartWithPaymentMethodsForRegisteredCustomer() - { - $reservedOrderId = 'test_order_item_with_items'; - $this->quoteResource->load( - $this->quote, - $reservedOrderId, - 'reserved_order_id' - ); - - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = $this->prepareGetCartQuery($maskedQuoteId); - - $response = $this->sendRequestWithToken($query); - - self::assertArrayHasKey('cart', $response); - self::assertNotEmpty($response['cart']['items']); - self::assertNotEmpty($response['cart']['available_payment_methods']); - self::assertCount(1, $response['cart']['available_payment_methods']); - self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); - self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php - */ - public function testGetCartWithPaymentMethodsForGuest() - { - $reservedOrderId = 'test_order_1'; - $this->quoteResource->load( - $this->quote, - $reservedOrderId, - 'reserved_order_id' - ); - - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $query = $this->prepareGetCartQuery($maskedQuoteId); - - $response = $this->graphQlQuery($query); - - self::assertArrayHasKey('cart', $response); - - self::assertNotEmpty($response['cart']['available_payment_methods']); - self::assertCount(2, $response['cart']['available_payment_methods']); - self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); - self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); - self::assertEquals('free', $response['cart']['available_payment_methods'][1]['code']); - self::assertEquals( - 'No Payment Information Required', - $response['cart']['available_payment_methods'][1]['title'] - ); - } - - /** - * Generates query for setting the specified shipping method on cart - * - * @param string $maskedQuoteId - * @return string - */ - private function prepareGetCartQuery( - string $maskedQuoteId - ) : string { - return <<<QUERY -{ - cart(cart_id: "$maskedQuoteId") { - applied_coupon { - code - } - items { - id - } - available_payment_methods { - code, - title - } - } -} - -QUERY; - } - - /** - * Sends a GraphQL request with using a bearer token - * - * @param string $query - * @return array - * @throws \Magento\Framework\Exception\AuthenticationException - */ - private function sendRequestWithToken(string $query): array - { - - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - - return $this->graphQlQuery($query, [], '', $headerMap); - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php new file mode 100644 index 0000000000000..4fd398439913e --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Guest; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; +use Magento\Quote\Api\GuestCartRepositoryInterface; + +/** + * Test for empty cart creation mutation + */ +class CreateEmptyCartTest extends GraphQlAbstract +{ + /** + * @var GuestCartRepositoryInterface + */ + private $guestCartRepository; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->guestCartRepository = $objectManager->get(GuestCartRepositoryInterface::class); + } + + public function testCreateEmptyCart() + { + $query = <<<QUERY +mutation { + createEmptyCart +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('createEmptyCart', $response); + + $maskedCartId = $response['createEmptyCart']; + $guestCart = $this->guestCartRepository->get($maskedCartId); + + self::assertNotNull($guestCart->getId()); + self::assertNull($guestCart->getCustomer()->getId()); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php new file mode 100644 index 0000000000000..d4459180bc16e --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php @@ -0,0 +1,90 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Guest; + +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for getting cart information + */ +class GetAvailablePaymentMethodsTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @inheritdoc + */ + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testGetCartWithPaymentMethods() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + available_payment_methods { + code + title + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response); + self::assertCount(2, $response['cart']['available_payment_methods']); + + self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); + self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); + + self::assertEquals('free', $response['cart']['available_payment_methods'][1]['code']); + self::assertEquals( + 'No Payment Information Required', + $response['cart']['available_payment_methods'][1]['title'] + ); + } + + /** + * @param string $reversedQuoteId + * @return string + */ + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } +} 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 new file mode 100644 index 0000000000000..42b5cbd06b9fc --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetCartTest.php @@ -0,0 +1,151 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Guest; + +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for getting cart information + */ +class GetCartTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + */ + public function testGetCart() + { + $maskedQuoteId = $this->unAssignCustomerFromQuote('test_order_item_with_items'); + $query = $this->getCartQuery($maskedQuoteId); + + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response); + self::assertEquals($maskedQuoteId, $response['cart']['cart_id']); + + self::assertArrayHasKey('items', $response['cart']); + self::assertCount(2, $response['cart']['items']); + + self::assertNotEmpty($response['cart']['items'][0]['id']); + self::assertEquals($response['cart']['items'][0]['qty'], 2); + self::assertEquals($response['cart']['items'][0]['product']['sku'], 'simple'); + + self::assertNotEmpty($response['cart']['items'][1]['id']); + self::assertEquals($response['cart']['items'][1]['qty'], 1); + self::assertEquals($response['cart']['items'][1]['product']['sku'], 'simple_one'); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + */ + public function testGetCustomerCart() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); + $query = $this->getCartQuery($maskedQuoteId); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"{$maskedQuoteId}\"" + ); + $this->graphQlQuery($query); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" + */ + public function testGetNonExistentCart() + { + $maskedQuoteId = 'non_existent_masked_id'; + $query = $this->getCartQuery($maskedQuoteId); + + $this->graphQlQuery($query); + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getCartQuery( + string $maskedQuoteId + ) : string { + return <<<QUERY +{ + cart(cart_id: "$maskedQuoteId") { + cart_id + items { + id + qty + product { + sku + } + } + } +} +QUERY; + } + + /** + * @param string $reversedQuoteId + * @return string + */ + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } + + /** + * @param string $reversedQuoteId + * @param int $customerId + * @return string + */ + private function unAssignCustomerFromQuote( + string $reversedQuoteId + ): string { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $quote->setCustomerId(0); + $this->quoteResource->save($quote); + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } +} 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 new file mode 100644 index 0000000000000..98c2fe81f82ab --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetBillingAddressOnCartTest.php @@ -0,0 +1,263 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Guest; + +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for set billing address on cart mutation + */ +class SetBillingAddressOnCartTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testSetNewBillingAddress() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + } + ) { + cart { + billing_address { + firstname + lastname + company + street + city + postcode + telephone + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + $cartResponse = $response['setBillingAddressOnCart']['cart']; + self::assertArrayHasKey('billing_address', $cartResponse); + $billingAddressResponse = $cartResponse['billing_address']; + $this->assertNewAddressFields($billingAddressResponse); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testSetNewBillingAddressWithUseForShippingParameter() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + use_for_shipping: true + } + } + ) { + cart { + billing_address { + firstname + lastname + company + street + city + postcode + telephone + } + shipping_addresses { + firstname + lastname + company + street + city + postcode + telephone + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response['setBillingAddressOnCart']); + $cartResponse = $response['setBillingAddressOnCart']['cart']; + self::assertArrayHasKey('billing_address', $cartResponse); + $billingAddressResponse = $cartResponse['billing_address']; + self::assertArrayHasKey('shipping_addresses', $cartResponse); + $shippingAddressResponse = current($cartResponse['shipping_addresses']); + $this->assertNewAddressFields($billingAddressResponse); + $this->assertNewAddressFields($shippingAddressResponse); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testSettBillingAddressToCustomerCart() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + address: { + firstname: "test firstname" + lastname: "test lastname" + company: "test company" + street: ["test street 1", "test street 2"] + city: "test city" + region: "test region" + postcode: "887766" + country_code: "US" + telephone: "88776655" + save_in_address_book: false + } + } + } + ) { + cart { + billing_address { + city + } + } + } +} +QUERY; + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$maskedQuoteId\"" + ); + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The current customer isn't authorized. + */ + public function testSetBillingAddressFromAddressBook() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = <<<QUERY +mutation { + setBillingAddressOnCart( + input: { + cart_id: "$maskedQuoteId" + billing_address: { + customer_address_id: 1 + } + } + ) { + cart { + billing_address { + city + } + } + } +} +QUERY; + $this->graphQlQuery($query); + } + + /** + * Verify the all the whitelisted fields for a New Address Object + * + * @param array $billingAddressResponse + */ + private function assertNewAddressFields(array $billingAddressResponse): void + { + $assertionMap = [ + ['response_field' => 'firstname', 'expected_value' => 'test firstname'], + ['response_field' => 'lastname', 'expected_value' => 'test lastname'], + ['response_field' => 'company', 'expected_value' => 'test company'], + ['response_field' => 'street', 'expected_value' => [0 => 'test street 1', 1 => 'test street 2']], + ['response_field' => 'city', 'expected_value' => 'test city'], + ['response_field' => 'postcode', 'expected_value' => '887766'], + ['response_field' => 'telephone', 'expected_value' => '88776655'] + ]; + + $this->assertResponseFields($billingAddressResponse, $assertionMap); + } + + /** + * @param string $reversedQuoteId + * @return string + */ + private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php similarity index 59% rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php index 89e88bd0ce04a..8286f97148953 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetPaymentMethodOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetPaymentMethodOnCartTest.php @@ -5,9 +5,8 @@ */ declare(strict_types=1); -namespace Magento\GraphQl\Quote; +namespace Magento\GraphQl\Quote\Guest; -use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\OfflinePayments\Model\Checkmo; use Magento\Quote\Model\QuoteFactory; use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; @@ -16,15 +15,10 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; /** - * Test for setting payment methods on cart + * Test for setting payment methods on cart by guest */ class SetPaymentMethodOnCartTest extends GraphQlAbstract { - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - /** * @var QuoteResource */ @@ -49,19 +43,38 @@ protected function setUp() $this->quoteResource = $objectManager->get(QuoteResource::class); $this->quoteFactory = $objectManager->get(QuoteFactory::class); $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php + */ + public function testSetPaymentWithVirtualProduct() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_virtual_product'); + $this->unAssignCustomerFromQuote('test_order_with_virtual_product'); + + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('setPaymentMethodOnCart', $response); + self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); + self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); + self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); + self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php */ - public function testSetPaymentMethodOnCart() + public function testSetPaymentWithSimpleProduct() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $this->unAssignCustomerFromQuote('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); - $response = $this->sendRequestWithToken($query); + $response = $this->graphQlQuery($query); self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); @@ -70,6 +83,20 @@ public function testSetPaymentMethodOnCart() self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); } + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @expectedException \Exception + * @expectedExceptionMessage The shipping address is missing. Set the address and try again. + */ + public function testSetPaymentWithSimpleProductWithoutAddress() + { + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; + $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + + $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); + $this->graphQlQuery($query); + } + /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php * @expectedException \Exception @@ -79,17 +106,18 @@ public function testSetNonExistingPaymentMethod() { $methodCode = 'noway'; $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); + $this->unAssignCustomerFromQuote('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); - $this->sendRequestWithToken($query); + $this->graphQlQuery($query); } /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php */ - public function testSetPaymentMethodByGuestToCustomerCart() + public function testSetPaymentMethodToCustomerCart() { - $methodCode = 'checkmo'; + $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); $query = $this->prepareMutationQuery($maskedQuoteId, $methodCode); @@ -97,7 +125,6 @@ public function testSetPaymentMethodByGuestToCustomerCart() $this->expectExceptionMessage( "The current user cannot perform operations on cart \"$maskedQuoteId\"" ); - $this->graphQlQuery($query); } @@ -116,9 +143,9 @@ private function prepareMutationQuery( mutation { setPaymentMethodOnCart(input: { - cart_id: "$maskedQuoteId", + cart_id: "{$maskedQuoteId}", payment_method: { - code: "$methodCode" + code: "{$methodCode}" } }) { @@ -134,18 +161,18 @@ private function prepareMutationQuery( } /** - * Sends a GraphQL request with using a bearer token - * - * @param string $query - * @return array - * @throws \Magento\Framework\Exception\AuthenticationException + * @param string $reversedQuoteId + * @param int $customerId + * @return string */ - private function sendRequestWithToken(string $query): array - { - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - - return $this->graphQlQuery($query, [], '', $headerMap); + private function unAssignCustomerFromQuote( + string $reversedQuoteId + ): string { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); + $quote->setCustomerId(0); + $this->quoteResource->save($quote); + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } /** diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php index c3685c88ae487..f73454b0a8b96 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/SetShippingAddressOnCartTest.php @@ -422,7 +422,7 @@ public function testSetMultipleNewShippingAddresses() */ public function testSetShippingAddressIfCustomerIsNotOwnerOfAddress() { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_with_simple_product_without_address'); + $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 2); $query = <<<QUERY mutation { From 27bc500e5d1800c13df9cd9b65f3d0628ed769c4 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 26 Feb 2019 17:55:57 -0600 Subject: [PATCH 215/254] GraphQL-293: [Payment methods] Set Payment Method on Cart -- Fixes after merge with mainline --- .../Customer/SetBillingAddressOnCartTest.php | 10 ++++++++-- .../Quote/Guest/SetBillingAddressOnCartTest.php | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) 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 2dd0394dfc6df..2e0b57f96fe3a 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 @@ -205,6 +205,10 @@ public function testSetBillingAddressFromAddressBook() city postcode telephone + country { + code + label + } } } } @@ -410,7 +414,8 @@ private function assertNewAddressFields(array $billingAddressResponse): void ['response_field' => 'street', 'expected_value' => [0 => 'test street 1', 1 => 'test street 2']], ['response_field' => 'city', 'expected_value' => 'test city'], ['response_field' => 'postcode', 'expected_value' => '887766'], - ['response_field' => 'telephone', 'expected_value' => '88776655'] + ['response_field' => 'telephone', 'expected_value' => '88776655'], + ['response_field' => 'country', 'expected_value' => ['code' => 'US', 'label' => 'US']], ]; $this->assertResponseFields($billingAddressResponse, $assertionMap); @@ -430,7 +435,8 @@ private function assertSavedBillingAddressFields(array $billingAddressResponse): ['response_field' => 'street', 'expected_value' => [0 => 'Green str, 67']], ['response_field' => 'city', 'expected_value' => 'CityM'], ['response_field' => 'postcode', 'expected_value' => '75477'], - ['response_field' => 'telephone', 'expected_value' => '3468676'] + ['response_field' => 'telephone', 'expected_value' => '3468676'], + ['response_field' => 'country', 'expected_value' => ['code' => 'US', 'label' => 'US']], ]; $this->assertResponseFields($billingAddressResponse, $assertionMap); 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 98c2fe81f82ab..24fc8353d2552 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 @@ -78,6 +78,10 @@ public function testSetNewBillingAddress() city postcode telephone + country { + code + label + } } } } @@ -130,6 +134,10 @@ public function testSetNewBillingAddressWithUseForShippingParameter() city postcode telephone + country { + code + label + } } shipping_addresses { firstname @@ -139,6 +147,10 @@ public function testSetNewBillingAddressWithUseForShippingParameter() city postcode telephone + country { + code + label + } } } } @@ -243,7 +255,8 @@ private function assertNewAddressFields(array $billingAddressResponse): void ['response_field' => 'street', 'expected_value' => [0 => 'test street 1', 1 => 'test street 2']], ['response_field' => 'city', 'expected_value' => 'test city'], ['response_field' => 'postcode', 'expected_value' => '887766'], - ['response_field' => 'telephone', 'expected_value' => '88776655'] + ['response_field' => 'telephone', 'expected_value' => '88776655'], + ['response_field' => 'country', 'expected_value' => ['code' => 'US', 'label' => 'US']], ]; $this->assertResponseFields($billingAddressResponse, $assertionMap); From c0c4bdf1a00cf6302ecf63c91467d607d96d1da5 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 26 Feb 2019 18:08:16 -0600 Subject: [PATCH 216/254] GraphQL-293: [Payment methods] Set Payment Method on Cart -- Fixes after merge with mainline --- .../SetPaymentMethodOnCartTest.php | 315 ------------------ 1 file changed, 315 deletions(-) delete mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/OfflinePayments/SetPaymentMethodOnCartTest.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflinePayments/SetPaymentMethodOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflinePayments/SetPaymentMethodOnCartTest.php deleted file mode 100644 index 3a3e50efa0676..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/OfflinePayments/SetPaymentMethodOnCartTest.php +++ /dev/null @@ -1,315 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\OfflinePayments; - -use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\OfflinePayments\Model\Banktransfer; -use Magento\OfflinePayments\Model\Cashondelivery; -use Magento\OfflinePayments\Model\Checkmo; -use Magento\OfflinePayments\Model\Purchaseorder; -use Magento\Quote\Model\QuoteFactory; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\ObjectManager; -use Magento\TestFramework\TestCase\GraphQlAbstract; -use Magento\Framework\App\Cache\TypeListInterface; -use Magento\Config\Model\ResourceModel\Config; - -/** - * Test for setting payment methods on cart - */ -class SetPaymentMethodOnCartTest extends GraphQlAbstract -{ - private const OFFLINE_METHOD_CODES = [ - Checkmo::PAYMENT_METHOD_CHECKMO_CODE, -// Banktransfer::PAYMENT_METHOD_BANKTRANSFER_CODE, -// Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE, -// Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE, - ]; - - /** - * @var CustomerTokenServiceInterface - */ - private $customerTokenService; - - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var QuoteFactory - */ - private $quoteFactory; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - - /** - * @var Config - */ - private $config; - - /** - * @var TypeListInterface - */ - private $cacheList; - - /** - * @inheritdoc - */ - protected function setUp() - { - $objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $objectManager->get(QuoteResource::class); - $this->quoteFactory = $objectManager->get(QuoteFactory::class); - $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); - $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); - $this->config = $objectManager->get(Config::class); - $this->cacheList = $objectManager->get(TypeListInterface::class); - - foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { - $this->config->saveConfig( - 'payment/' . $offlineMethodCode . '/active', - '1', - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - } - $this->cacheList->cleanType('config'); - } - - /** - * @inheritdoc - */ - protected function tearDown() - { -// foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { -// //Never no disable checkmo method -// if ($offlineMethodCode === Checkmo::PAYMENT_METHOD_CHECKMO_CODE) { -// continue; -// } -// $this->config->saveConfig( -// 'payment/' . $offlineMethodCode . '/active', -// '0', -// ScopeConfigInterface::SCOPE_TYPE_DEFAULT, -// 0 -// ); -// } -// $this->cacheList->cleanType('config'); - } - - /** - * @param string $methodCode - * @dataProvider dataProviderOfflinePaymentMethods - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetPaymentMethodOnCart(string $methodCode) - { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $methodCode - ); - - $response = $this->sendRequestWithToken($query); - - self::assertArrayHasKey('setPaymentMethodOnCart', $response); - self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); - self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); - self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); - } - - public function dataProviderOfflinePaymentMethods(): array - { - $methods = []; - foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) { - //Purchase order requires additional input and is tested separately - if ($offlineMethodCode === Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE) { - continue; - } - $methods[] = [$offlineMethodCode]; - } - - return $methods; - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetNonExistingPaymentMethod() - { - $paymentMethod = 'noway'; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $paymentMethod - ); - - $this->expectExceptionMessage('The requested Payment Method is not available.'); - $this->sendRequestWithToken($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetPaymentMethodByGuestToCustomerCart() - { - $paymentMethod = 'checkmo'; - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - $paymentMethod - ); - - $this->expectExceptionMessage( - "The current user cannot perform operations on cart \"$maskedQuoteId\"" - ); - - $this->graphQlQuery($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testSetPaymentMethodPurchaseOrderOnCart() - { - $methodCode = \Magento\OfflinePayments\Model\Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE; - $poNumber = 'GQL-19002'; - - /** @var \Magento\Config\Model\ResourceModel\Config $config */ - $config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class); - $config->saveConfig( - 'payment/' . $methodCode . '/active', - 1, - ScopeConfigInterface::SCOPE_TYPE_DEFAULT, - 0 - ); - - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); - - $query = <<<QUERY -mutation { - setPaymentMethodOnCart(input: - { - cart_id: "$maskedQuoteId", - payment_method: { - code: "$methodCode" - purchase_order_number: "$poNumber" - } - }) { - - cart { - cart_id, - selected_payment_method { - code - purchase_order_number - } - } - } -} - -QUERY; - - $response = $this->sendRequestWithToken($query); - - self::assertArrayHasKey('setPaymentMethodOnCart', $response); - self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); - self::assertArrayHasKey('payment_method', $response['setPaymentMethodOnCart']['cart']); - self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); - self::assertEquals( - $poNumber, - $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['purchase_order_number'] - ); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php - */ - public function testPurchaseOrderPaymentMethodFailingValidation() - { - $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); - - $query = $this->prepareMutationQuery( - $maskedQuoteId, - Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE - ); - - $this->expectExceptionMessage('Purchase order number is a required field.'); - $this->sendRequestWithToken($query); - } - - /** - * Generates query for setting the specified shipping method on cart - * - * @param string $maskedQuoteId - * @param string $methodCode - * @return string - */ - private function prepareMutationQuery( - string $maskedQuoteId, - string $methodCode - ) : string { - return <<<QUERY -mutation { - setPaymentMethodOnCart(input: - { - cart_id: "$maskedQuoteId", - payment_method: { - code: "$methodCode" - } - }) { - - cart { - cart_id, - selected_payment_method { - code - } - } - } -} - -QUERY; - } - - /** - * Sends a GraphQL request with using a bearer token - * - * @param string $query - * @return array - * @throws \Magento\Framework\Exception\AuthenticationException - */ - private function sendRequestWithToken(string $query): array - { - $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - - return $this->graphQlQuery($query, [], '', $headerMap); - } - - /** - * @param string $reversedQuoteId - * @return string - */ - private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string - { - $quote = $this->quoteFactory->create(); - $this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id'); - - return $this->quoteIdToMaskedId->execute((int)$quote->getId()); - } -} From 8d19d7c52a2efc3572d987f19286a1d1b505bfc7 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Tue, 26 Feb 2019 18:30:03 -0600 Subject: [PATCH 217/254] GraphQL-293: [Payment methods] Set Payment Method on Cart --- composer.json | 1 - .../GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php | 1 - .../GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php | 1 - 3 files changed, 3 deletions(-) diff --git a/composer.json b/composer.json index 2a2e1b2d85947..e6d073563b6e1 100644 --- a/composer.json +++ b/composer.json @@ -109,7 +109,6 @@ "magento/module-backend": "*", "magento/module-backup": "*", "magento/module-braintree": "*", - "magento/module-braintree-graph-ql": "*", "magento/module-bundle": "*", "magento/module-bundle-graph-ql": "*", "magento/module-bundle-import-export": "*", diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php index b5323ee986582..5695aab6854d4 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetAvailablePaymentMethodsTest.php @@ -71,7 +71,6 @@ public function testGetCartWithPaymentMethods() $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response); - self::assertCount(1, $response['cart']['available_payment_methods']); self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php index d4459180bc16e..a5a08aaf39fb1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetAvailablePaymentMethodsTest.php @@ -64,7 +64,6 @@ public function testGetCartWithPaymentMethods() $response = $this->graphQlQuery($query); self::assertArrayHasKey('cart', $response); - self::assertCount(2, $response['cart']['available_payment_methods']); self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); From 0e4ac3bc32dcbf6e2349babb6155feebdb96791e Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Tue, 26 Feb 2019 11:58:10 +0100 Subject: [PATCH 218/254] Elasticsearch6 implementation. --- .../AdvancedSearch/etc/adminhtml/system.xml | 6 +- .../System/Config/TestConnection.php | 32 + app/code/Magento/Elasticsearch6/LICENSE.txt | 48 ++ .../Magento/Elasticsearch6/LICENSE_AFL.txt | 48 ++ .../FieldName/Resolver/DefaultResolver.php | 34 ++ .../Model/Client/Elasticsearch.php | 332 +++++++++++ .../Magento/Elasticsearch6/Model/Config.php | 57 ++ .../Model/DataProvider/Suggestions.php | 271 +++++++++ app/code/Magento/Elasticsearch6/README.md | 2 + .../Resolver/DefaultResolverTest.php | 118 ++++ .../Unit/Model/Client/ElasticsearchTest.php | 562 ++++++++++++++++++ .../Model/DataProvider/SuggestionsTest.php | 183 ++++++ app/code/Magento/Elasticsearch6/composer.json | 26 + .../Elasticsearch6/etc/adminhtml/system.xml | 85 +++ .../Magento/Elasticsearch6/etc/config.xml | 20 + app/code/Magento/Elasticsearch6/etc/di.xml | 165 +++++ .../Magento/Elasticsearch6/etc/module.xml | 17 + .../Magento/Elasticsearch6/registration.php | 11 + 18 files changed, 2014 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php create mode 100644 app/code/Magento/Elasticsearch6/LICENSE.txt create mode 100644 app/code/Magento/Elasticsearch6/LICENSE_AFL.txt create mode 100644 app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php create mode 100644 app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php create mode 100644 app/code/Magento/Elasticsearch6/Model/Config.php create mode 100644 app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php create mode 100644 app/code/Magento/Elasticsearch6/README.md create mode 100644 app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolverTest.php create mode 100644 app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php create mode 100644 app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php create mode 100644 app/code/Magento/Elasticsearch6/composer.json create mode 100644 app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml create mode 100644 app/code/Magento/Elasticsearch6/etc/config.xml create mode 100644 app/code/Magento/Elasticsearch6/etc/di.xml create mode 100644 app/code/Magento/Elasticsearch6/etc/module.xml create mode 100644 app/code/Magento/Elasticsearch6/registration.php diff --git a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml index fa7774f5cec1d..2c4f7fca1834b 100644 --- a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml @@ -48,18 +48,18 @@ </depends> </field> <!--<group id="suggestions">--> - <field id="search_suggestion_enabled" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="search_suggestion_enabled" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Enable Search Suggestions</label> <comment>When you enable this option your site may slow down.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="search_suggestion_count" translate="label" type="text" sortOrder="71" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="search_suggestion_count" translate="label" type="text" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Search Suggestions Count</label> <depends> <field id="search_suggestion_enabled">1</field> </depends> </field> - <field id="search_suggestion_count_results_enabled" translate="label" type="select" sortOrder="72" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="search_suggestion_count_results_enabled" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Show Results Count for Each Suggestion</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>When you enable this option your site may slow down.</comment> diff --git a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php new file mode 100644 index 0000000000000..5573d959fa372 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\Block\Adminhtml\System\Config; + +/** + * Elasticsearch 6x 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_elasticsearch6_server_hostname', + 'port' => 'catalog_search_elasticsearch6_server_port', + 'index' => 'catalog_search_elasticsearch6_index_prefix', + 'enableAuth' => 'catalog_search_elasticsearch6_enable_auth', + 'username' => 'catalog_search_elasticsearch6_username', + 'password' => 'catalog_search_elasticsearch6_password', + 'timeout' => 'catalog_search_elasticsearch6_server_timeout', + ]; + + return array_merge(parent::_getFieldMapping(), $fields); + } +} diff --git a/app/code/Magento/Elasticsearch6/LICENSE.txt b/app/code/Magento/Elasticsearch6/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/Elasticsearch6/LICENSE.txt @@ -0,0 +1,48 @@ + +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/Elasticsearch6/LICENSE_AFL.txt b/app/code/Magento/Elasticsearch6/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +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/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php new file mode 100644 index 0000000000000..2420335a95dc5 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; + +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; + +/** + * Default name resolver. + */ +class DefaultResolver extends \Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver +{ + /** + * Get field name. + * + * @param AttributeAdapter $attribute + * @param array $context + * @return string + */ + public function getFieldName(AttributeAdapter $attribute, $context = []): ?string + { + $fieldName = parent::getFieldName($attribute, $context); + + if ($fieldName === '_all') { + $fieldName = '_search'; + } + + return $fieldName; + } +} diff --git a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php new file mode 100644 index 0000000000000..af39b24acda56 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php @@ -0,0 +1,332 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\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 = []) + { + $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); + $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); + if (!$protocol) { + $protocol = 'http'; + } + if (!empty($options['port'])) { + $host .= ':' . $options['port']; + } + if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) { + $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host); + } + + $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]); + } + + /** + * Exists alias. + * + * @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 => [ + 'properties' => [ + '_search' => [ + 'type' => 'text' + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => false, + 'copy_to' => '_search' + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'int', + ], + ], + ], + ], + ], + ], + ]; + + 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/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php new file mode 100644 index 0000000000000..aec00d92ce029 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/Config.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Elasticsearch6\Model; + +use Magento\Framework\Search\EngineResolverInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\AdvancedSearch\Model\Client\ClientResolver; + +/** + * Elasticsearch6 config model + */ +class Config extends \Magento\Elasticsearch\Model\Config +{ + /** + * Search engine name + */ + private const ENGINE_NAME_6 = 'elasticsearch6'; + + /** + * @var EngineResolverInterface + */ + private $engineResolver; + + /** + * Constructor + * + * @param ScopeConfigInterface $scopeConfig + * @param ClientResolver|null $clientResolver + * @param EngineResolverInterface|null $engineResolver + * @param string|null $prefix + */ + public function __construct( + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\AdvancedSearch\Model\Client\ClientResolver $clientResolver = null, + \Magento\Framework\Search\EngineResolverInterface $engineResolver = null, + $prefix = null + ) { + parent::__construct($scopeConfig, $clientResolver, $engineResolver, $prefix); + $this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class); + } + + /** + * Return true if third party search engine is used + * + * @return bool + * @since 100.1.0 + */ + public function isElasticsearchEnabled() + { + return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME_6]); + } +} diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php new file mode 100644 index 0000000000000..24412fa6baec8 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php @@ -0,0 +1,271 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\Model\DataProvider; + +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Search\Model\QueryInterface; +use Magento\AdvancedSearch\Model\SuggestedQueriesInterface; +use Magento\Elasticsearch6\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\Store\Model\StoreManagerInterface as StoreManager; + +/** + * Class Suggestions + */ +class Suggestions implements SuggestedQueriesInterface +{ + /** + * @var Config + */ + private $config; + + /** + * @var QueryResultFactory + */ + private $queryResultFactory; + + /** + * @var ConnectionManager + */ + private $connectionManager; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var SearchIndexNameResolver + */ + private $searchIndexNameResolver; + + /** + * @var StoreManager + */ + private $storeManager; + + /** + * @var FieldProviderInterface + */ + private $fieldProvider; + + /** + * Suggestions constructor. + * + * @param ScopeConfigInterface $scopeConfig + * @param Config $config + * @param QueryResultFactory $queryResultFactory + * @param ConnectionManager $connectionManager + * @param SearchIndexNameResolver $searchIndexNameResolver + * @param StoreManager $storeManager + * @param FieldProviderInterface $fieldProvider + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + Config $config, + QueryResultFactory $queryResultFactory, + ConnectionManager $connectionManager, + SearchIndexNameResolver $searchIndexNameResolver, + StoreManager $storeManager, + FieldProviderInterface $fieldProvider + ) { + $this->queryResultFactory = $queryResultFactory; + $this->connectionManager = $connectionManager; + $this->scopeConfig = $scopeConfig; + $this->config = $config; + $this->searchIndexNameResolver = $searchIndexNameResolver; + $this->storeManager = $storeManager; + $this->fieldProvider = $fieldProvider; + } + + /** + * @inheritdoc + */ + public function getItems(QueryInterface $query) + { + $result = []; + if ($this->isSuggestionsAllowed()) { + $isResultsCountEnabled = $this->isResultsCountEnabled(); + + foreach ($this->getSuggestions($query) as $suggestion) { + $count = null; + if ($isResultsCountEnabled) { + $count = isset($suggestion['freq']) ? $suggestion['freq'] : null; + } + $result[] = $this->queryResultFactory->create( + [ + 'queryText' => $suggestion['text'], + 'resultsCount' => $count, + ] + ); + } + } + + return $result; + } + + /** + * @inheritdoc + */ + public function isResultsCountEnabled() + { + return $this->scopeConfig->isSetFlag( + SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT_RESULTS_ENABLED, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * Get Suggestions + * + * @param QueryInterface $query + * + * @return array + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getSuggestions(QueryInterface $query) + { + $suggestions = []; + $searchSuggestionsCount = $this->getSearchSuggestionsCount(); + + $searchQuery = $this->initQuery($query); + $searchQuery = $this->addSuggestFields($searchQuery, $searchSuggestionsCount); + + $result = $this->fetchQuery($searchQuery); + + if (is_array($result)) { + foreach ($result['suggest'] ?? [] as $suggest) { + foreach ($suggest as $token) { + foreach ($token['options'] ?? [] as $key => $suggestion) { + $suggestions[$suggestion['score'] . '_' . $key] = $suggestion; + } + } + } + ksort($suggestions); + $texts = array_unique(array_column($suggestions, 'text')); + $suggestions = array_slice(array_intersect_key(array_values($suggestions), $texts), 0, $searchSuggestionsCount); + } + + return $suggestions; + } + + /** + * Init Search Query + * + * @param string $query + * + * @return array + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function initQuery($query) + { + $searchQuery = [ + 'index' => $this->searchIndexNameResolver->getIndexName( + $this->storeManager->getStore()->getId(), + Config::ELASTICSEARCH_TYPE_DEFAULT + ), + 'type' => Config::ELASTICSEARCH_TYPE_DEFAULT, + 'body' => [ + 'suggest' => [ + 'text' => $query->getQueryText() + ] + ], + ]; + + return $searchQuery; + } + + /** + * Build Suggest on searchable fields. + * + * @param array $searchQuery + * @param int $searchSuggestionsCount + * + * @return array + */ + private function addSuggestFields($searchQuery, $searchSuggestionsCount) + { + $fields = $this->getSuggestFields(); + foreach ($fields as $field) { + $searchQuery['body']['suggest']['phrase_' . $field] = [ + 'phrase' => [ + 'field' => $field, + 'analyzer' => 'standard', + 'size' => $searchSuggestionsCount, + 'max_errors' => 1, + 'direct_generator' => [ + [ + 'field' => $field, + 'min_word_length' => 3, + 'min_doc_freq' => 1, + ] + ], + ], + ]; + } + + return $searchQuery; + } + + /** + * Get fields to build suggest query on. + * + * @return array + */ + private function getSuggestFields() + { + $fields = array_filter($this->fieldProvider->getFields(), function ($field) { + return (($field['type'] ?? null) === 'text') && (($field['index'] ?? null) !== false); + }); + + return array_keys($fields); + } + + /** + * Fetch Query + * + * @param array $query + * @return array + */ + private function fetchQuery(array $query) + { + return $this->connectionManager->getConnection()->query($query); + } + + /** + * Get search suggestions Max Count from config + * + * @return int + */ + private function getSearchSuggestionsCount() + { + return (int) $this->scopeConfig->getValue( + SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * Is Search Suggestions Allowed + * + * @return bool + */ + private function isSuggestionsAllowed() + { + $isSuggestionsEnabled = $this->scopeConfig->isSetFlag( + SuggestedQueriesInterface::SEARCH_SUGGESTION_ENABLED, + ScopeInterface::SCOPE_STORE + ); + $isEnabled = $this->config->isElasticsearchEnabled(); + $isSuggestionsAllowed = ($isEnabled && $isSuggestionsEnabled); + + return $isSuggestionsAllowed; + } +} diff --git a/app/code/Magento/Elasticsearch6/README.md b/app/code/Magento/Elasticsearch6/README.md new file mode 100644 index 0000000000000..8bf95ad95d147 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/README.md @@ -0,0 +1,2 @@ +Magento\Elasticsearch module allows to use Elastic search engine (v6) for product searching capabilities. +The module implements Magento\Search library interfaces. diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolverTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolverTest.php new file mode 100644 index 0000000000000..c2a70d3f082f0 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolverTest.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch6\Test\Unit\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; + +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface + as FieldTypeResolver; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface + as FieldTypeConverterInterface; +use Magento\Elasticsearch6\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver; + +/** + * @SuppressWarnings(PHPMD) + */ +class DefaultResolverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var DefaultResolver + */ + private $resolver; + + /** + * @var FieldTypeResolver + */ + private $fieldTypeResolver; + + /** + * @var FieldTypeConverterInterface + */ + private $fieldTypeConverter; + + /** + * Set up test environment + * + * @return void + */ + protected function setUp() + { + $objectManager = new ObjectManagerHelper($this); + $this->fieldTypeResolver = $this->getMockBuilder(FieldTypeResolver::class) + ->disableOriginalConstructor() + ->setMethods(['getFieldType']) + ->getMockForAbstractClass(); + $this->fieldTypeConverter = $this->getMockBuilder(FieldTypeConverterInterface::class) + ->disableOriginalConstructor() + ->setMethods(['convert']) + ->getMockForAbstractClass(); + + $this->resolver = $objectManager->getObject( + DefaultResolver::class, + [ + 'fieldTypeResolver' => $this->fieldTypeResolver, + 'fieldTypeConverter' => $this->fieldTypeConverter + ] + ); + } + + /** + * @dataProvider getFieldNameProvider + * @param $fieldType + * @param $attributeCode + * @param $frontendInput + * @param $context + * @param $expected + * @return void + */ + public function testGetFieldName( + $fieldType, + $attributeCode, + $frontendInput, + $context, + $expected + ) { + $this->fieldTypeConverter->expects($this->any()) + ->method('convert') + ->willReturn('string'); + $attributeMock = $this->getMockBuilder(AttributeAdapter::class) + ->disableOriginalConstructor() + ->setMethods(['getAttributeCode', 'getFrontendInput']) + ->getMock(); + $attributeMock->expects($this->any()) + ->method('getAttributeCode') + ->willReturn($attributeCode); + $attributeMock->expects($this->any()) + ->method('getFrontendInput') + ->willReturn($frontendInput); + $this->fieldTypeResolver->expects($this->any()) + ->method('getFieldType') + ->willReturn($fieldType); + + $this->assertEquals( + $expected, + $this->resolver->getFieldName($attributeMock, $context) + ); + } + + /** + * @return array + */ + public function getFieldNameProvider() + { + return [ + ['', 'code', '', [], 'code'], + ['', 'code', '', ['type' => 'default'], 'code'], + ['string', '*', '', ['type' => 'default'], '_search'], + ['', 'code', '', ['type' => 'default'], 'code'], + ['', 'code', 'select', ['type' => 'default'], 'code'], + ['', 'code', 'boolean', ['type' => 'default'], 'code'], + ['', 'code', '', ['type' => 'type'], 'sort_code'], + ]; + } +} diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php new file mode 100644 index 0000000000000..8276d0dd8dbe8 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php @@ -0,0 +1,562 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\Test\Unit\Model\Client; + +use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class ElasticsearchTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ElasticsearchClient + */ + protected $model; + + /** + * @var \Elasticsearch\Client|\PHPUnit_Framework_MockObject_MockObject + */ + protected $elasticsearchClientMock; + + /** + * @var \Elasticsearch\Namespaces\IndicesNamespace|\PHPUnit_Framework_MockObject_MockObject + */ + protected $indicesMock; + + /** + * @var ObjectManagerHelper + */ + protected $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' => '6.0.0']]); + + $this->objectManager = new ObjectManagerHelper($this); + $this->model = $this->objectManager->getObject( + \Magento\Elasticsearch6\Model\Client\Elasticsearch::class, + [ + 'options' => $this->getOptions(), + 'elasticsearchClient' => $this->elasticsearchClientMock + ] + ); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testConstructorOptionsException() + { + $result = $this->objectManager->getObject( + \Magento\Elasticsearch6\Model\Client\Elasticsearch::class, + [ + 'options' => [] + ] + ); + $this->assertNotNull($result); + } + + /** + * Test client creation from the list of options + */ + public function testConstructorWithOptions() + { + $result = $this->objectManager->getObject( + \Magento\Elasticsearch6\Model\Client\Elasticsearch::class, + [ + 'options' => $this->getOptions() + ] + ); + $this->assertNotNull($result); + } + + /** + * 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\Elasticsearch6\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', + 'body' => [ + 'product' => [ + 'properties' => [ + '_search' => [ + 'type' => 'text', + ], + 'name' => [ + 'type' => 'text', + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => false, + 'copy_to' => '_search' + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'int', + ], + ], + ], + ], + ], + ], + ]); + $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', + 'body' => [ + 'product' => [ + 'properties' => [ + '_search' => [ + 'type' => 'text', + ], + 'name' => [ + 'type' => 'text', + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => false, + 'copy_to' => '_search' + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'int', + ], + ], + ], + ], + ], + ], + ]) + ->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 + */ + protected function getEmptyIndexOption() + { + return [ + 'hostname' => 'localhost', + 'port' => '9200', + 'index' => '', + 'timeout' => 15, + 'enableAuth' => 1, + 'username' => 'user', + 'password' => 'passwd', + ]; + } +} diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php new file mode 100644 index 0000000000000..957edc559fdcb --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php @@ -0,0 +1,183 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\Test\Unit\Model\DataProvider; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Elasticsearch\Model\DataProvider\Suggestions; +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\Store\Model\StoreManagerInterface as StoreManager; +use Magento\Search\Model\QueryInterface; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SuggestionsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Suggestions + */ + private $model; + + /** + * @var Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $config; + + /** + * @var QueryResultFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $queryResultFactory; + + /** + * @var ConnectionManager|\PHPUnit_Framework_MockObject_MockObject + */ + private $connectionManager; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; + + /** + * @var SearchIndexNameResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private $searchIndexNameResolver; + + /** + * @var StoreManager|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeManager; + + /** + * @var QueryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $query; + + /** + * Set up test environment + * + * @return void + */ + protected function setUp() + { + $this->config = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Config::class) + ->disableOriginalConstructor() + ->setMethods(['isElasticsearchEnabled']) + ->getMock(); + + $this->queryResultFactory = $this->getMockBuilder(\Magento\Search\Model\QueryResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->connectionManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) + ->disableOriginalConstructor() + ->setMethods(['getConnection']) + ->getMock(); + + $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->searchIndexNameResolver = $this + ->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver::class) + ->disableOriginalConstructor() + ->setMethods(['getIndexName']) + ->getMock(); + + $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->query = $this->getMockBuilder(\Magento\Search\Model\QueryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $objectManager = new ObjectManagerHelper($this); + + $this->model = $objectManager->getObject( + \Magento\Elasticsearch6\Model\DataProvider\Suggestions::class, + [ + 'queryResultFactory' => $this->queryResultFactory, + 'connectionManager' => $this->connectionManager, + 'scopeConfig' => $this->scopeConfig, + 'config' => $this->config, + 'searchIndexNameResolver' => $this->searchIndexNameResolver, + 'storeManager' => $this->storeManager + ] + ); + } + + /** + * Test getItems() method + */ + public function testGetItems() + { + $this->scopeConfig->expects($this->any()) + ->method('getValue') + ->willReturn(1); + + $this->config->expects($this->any()) + ->method('isElasticsearchEnabled') + ->willReturn(1); + + $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->storeManager->expects($this->any()) + ->method('getStore') + ->willReturn($store); + + $store->expects($this->any()) + ->method('getId') + ->willReturn(1); + + $this->searchIndexNameResolver->expects($this->any()) + ->method('getIndexName') + ->willReturn('magento2_product_1'); + + $this->query->expects($this->any()) + ->method('getQueryText') + ->willReturn('query'); + + $client = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Client\Elasticsearch::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->connectionManager->expects($this->any()) + ->method('getConnection') + ->willReturn($client); + + $client->expects($this->any()) + ->method('query') + ->willReturn([ + 'suggest' => [ + 'phrase_field' => [ + 'options' => [ + 'text' => 'query', + 'score' => 1, + 'freq' => 1, + ] + ], + ], + ]); + + $query = $this->getMockBuilder(\Magento\Search\Model\QueryResult::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->queryResultFactory->expects($this->any()) + ->method('create') + ->willReturn($query); + + $this->assertInternalType('array', $this->model->getItems($this->query)); + } +} diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json new file mode 100644 index 0000000000000..ec9fc3b463f5d --- /dev/null +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-elasticsearch-6", + "description": "N/A", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-elasticsearch": ">=100.3.0", + "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" + }, + "suggest": { + "magento/module-config": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\Elasticsearch6\\": "" + } + } +} diff --git a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..067a0acb8c908 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml @@ -0,0 +1,85 @@ +<?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 6.0+ --> + <field id="elasticsearch6_server_hostname" translate="label" type="text" sortOrder="71" + showInDefault="1" showInWebsite="0" showInStore="0"> + <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"> + <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"> + <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"> + <label>Enable Elasticsearch HTTP Auth</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="engine">elasticsearch6</field> + </depends> + </field> + + <field id="elasticsearch6_username" translate="label" type="text" sortOrder="75" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch HTTP Username</label> + <depends> + <field id="engine">elasticsearch6</field> + <field id="elasticsearch6_enable_auth">1</field> + </depends> + </field> + + <field id="elasticsearch6_password" translate="label" type="text" sortOrder="76" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch HTTP Password</label> + <depends> + <field id="engine">elasticsearch6</field> + <field id="elasticsearch6_enable_auth">1</field> + </depends> + </field> + + <field id="elasticsearch6_server_timeout" translate="label" type="text" sortOrder="77" showInDefault="1" + showInWebsite="0" showInStore="0"> + <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"> + <label/> + <button_label>Test Connection</button_label> + <frontend_model>Magento\Elasticsearch6\Block\Adminhtml\System\Config\TestConnection</frontend_model> + <depends> + <field id="engine">elasticsearch6</field> + </depends> + </field> + </group> + </section> + </system> +</config> diff --git a/app/code/Magento/Elasticsearch6/etc/config.xml b/app/code/Magento/Elasticsearch6/etc/config.xml new file mode 100644 index 0000000000000..047ae977fdef1 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/etc/config.xml @@ -0,0 +1,20 @@ +<?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> + <elasticsearch6_server_hostname>localhost</elasticsearch6_server_hostname> + <elasticsearch6_server_port>9200</elasticsearch6_server_port> + <elasticsearch6_index_prefix>magento2</elasticsearch6_index_prefix> + <elasticsearch6_enable_auth>0</elasticsearch6_enable_auth> + <elasticsearch6_server_timeout>15</elasticsearch6_server_timeout> + </search> + </catalog> + </default> +</config> diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml new file mode 100644 index 0000000000000..25eff42fd3442 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -0,0 +1,165 @@ +<?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\Search\Model\Adminhtml\System\Config\Source\Engine"> + <arguments> + <argument name="engines" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">Elasticsearch 6.0+</item> + </argument> + </arguments> + </type> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProviderProxy"> + <arguments> + <argument name="categoryFieldsProviders" xsi:type="array"> + <item name="elasticsearch6" 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="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"> + <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Client\ElasticsearchFactory</item> + </argument> + <argument name="clientOptions" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Config</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory"> + <arguments> + <argument name="handlers" xsi:type="array"> + <item name="elasticsearch6" 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="elasticsearch6" 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="elasticsearch6" 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="elasticsearch6" xsi:type="string">Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter</item> + </argument> + </arguments> + </type> + + <type name="Magento\Search\Model\EngineResolver"> + <arguments> + <argument name="engines" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">elasticsearch6</item> + </argument> + </arguments> + </type> + + <virtualType name="Magento\Elasticsearch6\Model\Client\ElasticsearchFactory" type="Magento\AdvancedSearch\Model\Client\ClientFactory"> + <arguments> + <argument name="clientClass" xsi:type="string">Magento\Elasticsearch6\Model\Client\Elasticsearch</argument> + </arguments> + </virtualType> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Client\ClientFactoryProxy"> + <arguments> + <argument name="clientFactories" xsi:type="array"> + <item name="elasticsearch6" xsi:type="object">Magento\Elasticsearch6\Model\Client\ElasticsearchFactory</item> + </argument> + </arguments> + </type> + + <type name="Magento\Framework\Search\Dynamic\IntervalFactory"> + <arguments> + <argument name="intervals" xsi:type="array"> + <item name="elasticsearch6" 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="elasticsearch6" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Dynamic\DataProvider</item> + </argument> + </arguments> + </type> + + + <type name="Magento\AdvancedSearch\Model\SuggestedQueries"> + <arguments> + <argument name="data" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">Magento\Elasticsearch6\Model\DataProvider\Suggestions</item> + </argument> + </arguments> + </type> + + <type name="Magento\Elasticsearch6\Model\DataProvider\Suggestions"> + <arguments> + <argument name="fieldProvider" xsi:type="object">elasticsearch5FieldProvider</argument> + </arguments> + </type> + + <virtualType name="elasticsearch6FieldNameResolver" 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\Elasticsearch6\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver</item> + </argument> + </arguments> + </virtualType> + + <virtualType name="Magento\Elasticsearch6\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">elasticsearch6FieldNameResolver</argument> + </arguments> + </virtualType> + + <type name="Magento\Search\Model\Search\PageSizeProvider"> + <arguments> + <argument name="pageSizeBySearchEngine" xsi:type="array"> + <item name="elasticsearch6" xsi:type="number">2147483647</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Elasticsearch6/etc/module.xml b/app/code/Magento/Elasticsearch6/etc/module.xml new file mode 100644 index 0000000000000..8756793621aa8 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/etc/module.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:Module/etc/module.xsd"> + <module name="Magento_Elasticsearch6"> + <sequence> + <module name="Magento_CatalogSearch"/> + <module name="Magento_Search"/> + <module name="Magento_AdvancedSearch"/> + <module name="Magento_Elasticsearch"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/Elasticsearch6/registration.php b/app/code/Magento/Elasticsearch6/registration.php new file mode 100644 index 0000000000000..7ab10e996eb8c --- /dev/null +++ b/app/code/Magento/Elasticsearch6/registration.php @@ -0,0 +1,11 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_Elasticsearch6', + __DIR__ +); From 57ecd34e2a86ba3f682304d5ade6bb02466833ef Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Tue, 26 Feb 2019 12:28:56 +0100 Subject: [PATCH 219/254] Allow more recent version of ES client. --- composer.json | 2 +- composer.lock | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index e6d073563b6e1..1c418fb38257e 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "colinmollenhour/credis": "1.10.0", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", - "elasticsearch/elasticsearch": "~2.0|~5.1", + "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", "magento/composer": "~1.4.0", "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "~1.14.1", diff --git a/composer.lock b/composer.lock index 656dbb94ed52f..7fe110b8dfaef 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": "87ae97b2da2504eaa90e4f56a3b968cb", + "content-hash": "4024498874329e8da2042e7ed9e3bd2f", "packages": [ { "name": "braintree/braintree_php", @@ -535,29 +535,31 @@ }, { "name": "elasticsearch/elasticsearch", - "version": "v5.4.0", + "version": "v6.1.0", "source": { "type": "git", "url": "https://github.com/elastic/elasticsearch-php.git", - "reference": "d3c5b55ad94f5053ca76c48585b4cde2cdc6bc59" + "reference": "b237a37b2cdf23a5a17fd3576cdea771394ad00d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/d3c5b55ad94f5053ca76c48585b4cde2cdc6bc59", - "reference": "d3c5b55ad94f5053ca76c48585b4cde2cdc6bc59", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/b237a37b2cdf23a5a17fd3576cdea771394ad00d", + "reference": "b237a37b2cdf23a5a17fd3576cdea771394ad00d", "shasum": "" }, "require": { + "ext-json": ">=1.3.7", "guzzlehttp/ringphp": "~1.0", - "php": "^5.6|^7.0", + "php": "^7.0", "psr/log": "~1.0" }, "require-dev": { "cpliakas/git-wrapper": "~1.0", "doctrine/inflector": "^1.1", "mockery/mockery": "0.9.4", - "phpunit/phpunit": "^4.7|^5.4", - "sami/sami": "~3.2", + "phpstan/phpstan-shim": "0.8.3", + "phpunit/phpunit": "6.3.0", + "squizlabs/php_codesniffer": "3.0.2", "symfony/finder": "^2.8", "symfony/yaml": "^2.8" }, @@ -586,7 +588,7 @@ "elasticsearch", "search" ], - "time": "2019-01-08T18:57:00+00:00" + "time": "2019-01-08T18:53:46+00:00" }, { "name": "guzzlehttp/ringphp", @@ -7813,6 +7815,7 @@ "mock", "xunit" ], + "abandoned": true, "time": "2018-08-09T05:50:03+00:00" }, { From ba236f9046b4b4a4e8d47efbfa06d4480c8644b8 Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Tue, 26 Feb 2019 18:16:36 +0100 Subject: [PATCH 220/254] Update composer.json replace. --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 1c418fb38257e..9ef896c7e72cc 100644 --- a/composer.json +++ b/composer.json @@ -149,6 +149,7 @@ "magento/module-downloadable-import-export": "*", "magento/module-eav": "*", "magento/module-elasticsearch": "*", + "magento/module-elasticsearch-6": "*", "magento/module-email": "*", "magento/module-encryption-key": "*", "magento/module-fedex": "*", From bdb032d0b3099fdaf083ea4b663ea746bed2f329 Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Wed, 27 Feb 2019 15:57:54 +0100 Subject: [PATCH 221/254] Update module dependencies. --- app/code/Magento/Elasticsearch6/composer.json | 4 ++++ app/code/Magento/Elasticsearch6/etc/module.xml | 1 + 2 files changed, 5 insertions(+) diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json index ec9fc3b463f5d..6288aff8490bb 100644 --- a/app/code/Magento/Elasticsearch6/composer.json +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -4,6 +4,10 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-advanced-search": "*", + "magento/module-catalog-search": "*", + "magento/module-search": "*", + "magento/module-store": "*", "magento/module-elasticsearch": ">=100.3.0", "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" }, diff --git a/app/code/Magento/Elasticsearch6/etc/module.xml b/app/code/Magento/Elasticsearch6/etc/module.xml index 8756793621aa8..4fde2394dfbdd 100644 --- a/app/code/Magento/Elasticsearch6/etc/module.xml +++ b/app/code/Magento/Elasticsearch6/etc/module.xml @@ -11,6 +11,7 @@ <module name="Magento_CatalogSearch"/> <module name="Magento_Search"/> <module name="Magento_AdvancedSearch"/> + <module name="Magento_Store"/> <module name="Magento_Elasticsearch"/> </sequence> </module> From b2561d97651c3b777786b8476699f63313f01fb3 Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Wed, 27 Feb 2019 16:00:06 +0100 Subject: [PATCH 222/254] Remove excessive since. --- app/code/Magento/Elasticsearch6/Model/Config.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php index aec00d92ce029..f21101f001d33 100644 --- a/app/code/Magento/Elasticsearch6/Model/Config.php +++ b/app/code/Magento/Elasticsearch6/Model/Config.php @@ -48,7 +48,6 @@ public function __construct( * Return true if third party search engine is used * * @return bool - * @since 100.1.0 */ public function isElasticsearchEnabled() { From d49a776362313d115932b9be2df9060acab704ef Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Wed, 27 Feb 2019 16:02:29 +0100 Subject: [PATCH 223/254] Set dependencies as required. --- app/code/Magento/Elasticsearch6/Model/Config.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php index f21101f001d33..1a989e2705fdd 100644 --- a/app/code/Magento/Elasticsearch6/Model/Config.php +++ b/app/code/Magento/Elasticsearch6/Model/Config.php @@ -36,12 +36,12 @@ class Config extends \Magento\Elasticsearch\Model\Config */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\AdvancedSearch\Model\Client\ClientResolver $clientResolver = null, - \Magento\Framework\Search\EngineResolverInterface $engineResolver = null, + \Magento\AdvancedSearch\Model\Client\ClientResolver $clientResolver, + \Magento\Framework\Search\EngineResolverInterface $engineResolver, $prefix = null ) { parent::__construct($scopeConfig, $clientResolver, $engineResolver, $prefix); - $this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class); + $this->engineResolver = $engineResolver; } /** From 07e3cf5dca37824d5b7a0ef594a2a6cea5bc67b1 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 27 Feb 2019 22:45:35 +0200 Subject: [PATCH 224/254] MSI-1988 issue fixed: Database Rollback not working M2.3 --- app/code/Magento/Backup/Model/Db.php | 24 +++- .../ResourceModel/Table/GetListTables.php | 42 +++++++ .../Model/ResourceModel/View/GetListViews.php | 41 +++++++ .../ResourceModel/View/GetViewsBackup.php | 106 ++++++++++++++++++ 4 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php create mode 100644 app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php create mode 100644 app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php diff --git a/app/code/Magento/Backup/Model/Db.php b/app/code/Magento/Backup/Model/Db.php index bc458a0a8e4bf..a385a476845db 100644 --- a/app/code/Magento/Backup/Model/Db.php +++ b/app/code/Magento/Backup/Model/Db.php @@ -6,6 +6,8 @@ namespace Magento\Backup\Model; use Magento\Backup\Helper\Data as Helper; +use Magento\Backup\Model\ResourceModel\Table\GetListTables; +use Magento\Backup\Model\ResourceModel\View\GetViewsBackup; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\RuntimeException; @@ -43,19 +45,35 @@ class Db implements \Magento\Framework\Backup\Db\BackupDbInterface */ private $helper; + /** + * @var GetListTables + */ + private $getListTables; + + /** + * @var GetViewsBackup + */ + private $getViewsBackup; + /** * @param \Magento\Backup\Model\ResourceModel\Db $resourceDb * @param \Magento\Framework\App\ResourceConnection $resource + * @param GetListTables|null $getListTables + * @param GetViewsBackup|null $getViewsBackup * @param Helper|null $helper */ public function __construct( \Magento\Backup\Model\ResourceModel\Db $resourceDb, \Magento\Framework\App\ResourceConnection $resource, - ?Helper $helper = null + ?Helper $helper = null, + GetListTables $getListTables = null, + GetViewsBackup $getViewsBackup = null ) { $this->_resourceDb = $resourceDb; $this->_resource = $resource; $this->helper = $helper ?? ObjectManager::getInstance()->get(Helper::class); + $this->getListTables = $getListTables ?: ObjectManager::getInstance()->get(GetListTables::class); + $this->getViewsBackup = $getViewsBackup ?: ObjectManager::getInstance()->get(GetViewsBackup::class); } /** @@ -161,7 +179,7 @@ public function createBackup(\Magento\Framework\Backup\Db\BackupInterface $backu $this->getResource()->beginTransaction(); - $tables = $this->getResource()->getTables(); + $tables = $this->getListTables->execute(); $backup->write($this->getResource()->getHeader()); @@ -198,6 +216,8 @@ public function createBackup(\Magento\Framework\Backup\Db\BackupInterface $backu $backup->write($this->getResource()->getTableDataAfterSql($table)); } } + $this->getViewsBackup->execute($backup); + $backup->write($this->getResource()->getTableForeignKeysSql()); $backup->write($this->getResource()->getTableTriggersSql()); $backup->write($this->getResource()->getFooter()); diff --git a/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php new file mode 100644 index 0000000000000..5a9f96a50c35a --- /dev/null +++ b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backup\Model\ResourceModel\Table; + +use Magento\Framework\App\ResourceConnection; + +/** + * Class GetListTables + */ +class GetListTables +{ + private const TABLE_TYPE = 'BASE TABLE'; + + /** + * @var ResourceConnection + */ + private $resource; + + /** + * @param ResourceConnection $resource + */ + public function __construct(ResourceConnection $resource) + { + $this->resource = $resource; + } + + /** + * @return array + */ + public function execute() + { + return $this->resource->getConnection('backup')->fetchCol( +"SHOW FULL TABLES WHERE `Table_type` = ?", + self::TABLE_TYPE + ); + } +} diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php new file mode 100644 index 0000000000000..54f2a1a65d280 --- /dev/null +++ b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backup\Model\ResourceModel\View; + +use Magento\Framework\App\ResourceConnection; + +/** + * Class GetListViews + */ +class GetListViews +{ + private const TABLE_TYPE = 'VIEW'; + /** + * @var ResourceConnection + */ + private $resource; + + /** + * @param ResourceConnection $resource + */ + public function __construct(ResourceConnection $resource) + { + $this->resource = $resource; + } + + /** + * @return array + */ + public function execute() + { + return $this->resource->getConnection('backup')->fetchCol( + "SHOW FULL TABLES WHERE `Table_type` = ?", + self::TABLE_TYPE + ); + } +} diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php b/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php new file mode 100644 index 0000000000000..bda5ff58c0594 --- /dev/null +++ b/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backup\Model\ResourceModel\View; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Backup\Db\BackupInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; + +/** + * Class GetViewsBackup + */ +class GetViewsBackup +{ + /** + * @var GetListViews + */ + private $getListViews; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var AdapterInterface + */ + private $connection; + + /** + * @param GetListViews $getListViews + * @param ResourceConnection $resourceConnection + */ + public function __construct( + GetListViews $getListViews, + ResourceConnection $resourceConnection + ) { + $this->getListViews = $getListViews; + $this->resourceConnection = $resourceConnection; + } + + /** + * @param BackupInterface $backup + */ + public function execute(BackupInterface $backup) + { + $views = $this->getListViews->execute(); + + foreach ($views as $view) { + $backup->write($this->getViewHeader($view)); + $backup->write($this->getDropViewSql($view)); + $backup->write($this->getShowCreateView($view)); + } + } + + /** + * @return AdapterInterface + */ + private function getConnection() + { + if (!$this->connection) { + $this->connection = $this->resourceConnection->getConnection('backup'); + } + + return $this->connection; + } + + /** + * @param string $viewName + * @return string + */ + private function getShowCreateView($viewName) + { + $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); + $query = 'SHOW CREATE VIEW ' . $quotedViewName; + $row = $this->getConnection()->fetchRow($query); + $regExp = '/\sDEFINER\=\`([^`]*)\`\@\`([^`]*)\`/'; + $sql = preg_replace($regExp, '', $row['Create View']); + + return $sql . ';' . "\n"; + } + + /** + * @param string $viewName + * @return string + */ + public function getViewHeader($viewName) + { + $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); + return "\n--\n" . "-- Structure for view {$quotedViewName}\n" . "--\n\n"; + } + + /** + * @param string $viewName + * @return string + */ + public function getDropViewSql($viewName) + { + $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); + return sprintf('DROP VIEW IF EXISTS %s;' . "\n", $quotedViewName); + } +} From 8c2ac6d426d21d2e897bfaa1e7c8b51ff0048e9c Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 27 Feb 2019 16:09:52 -0600 Subject: [PATCH 225/254] MAGETWO-98357: The required product attribute is not displayed when change attribute set - CR feedback --- .../Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml index 73280249eb68d..3027416ee520b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml @@ -46,6 +46,7 @@ <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"> From 2f3fdc32382baaf51ceb336fc8bf7ef5fdc5a83f Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Thu, 28 Feb 2019 07:56:03 +0200 Subject: [PATCH 226/254] Notice: Undefined index: shipping_carrier_code is returned instead of Required parameter "shipping_carrier_code" is missin --- .../QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php index be957835a230d..882ff2ff1b37b 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php @@ -72,10 +72,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value if (!isset($shippingMethod['cart_address_id']) || empty($shippingMethod['cart_address_id'])) { throw new GraphQlInputException(__('Required parameter "cart_address_id" is missing')); } - if (!!isset($shippingMethod['carrier_code']) || empty($shippingMethod['carrier_code'])) { + if (!isset($shippingMethod['carrier_code']) || empty($shippingMethod['carrier_code'])) { throw new GraphQlInputException(__('Required parameter "shipping_carrier_code" is missing')); } - if (!!isset($shippingMethod['method_code']) || empty($shippingMethod['method_code'])) { + if (!isset($shippingMethod['method_code']) || empty($shippingMethod['method_code'])) { throw new GraphQlInputException(__('Required parameter "shipping_method_code" is missing')); } From 03f3a4ac093a3c38f03f9eb62191d314f418b209 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 28 Feb 2019 17:09:09 +0000 Subject: [PATCH 227/254] magento/magento2#21458: Fixed static tests --- .../Block/Adminhtml/System/Config/TestConnection.php | 2 +- .../FieldProvider/FieldName/Resolver/DefaultResolver.php | 3 ++- .../Elasticsearch6/Model/DataProvider/Suggestions.php | 8 ++++++-- .../Magento/Test/Php/_files/phpcpd/blacklist/common.txt | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php index 5573d959fa372..8f99ef14dc646 100644 --- a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php +++ b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php @@ -12,7 +12,7 @@ class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection { /** - * {@inheritdoc} + * @inheritdoc */ protected function _getFieldMapping() { diff --git a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php index 2420335a95dc5..7532927f1dc85 100644 --- a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php +++ b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php @@ -8,11 +8,12 @@ namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver as Base; /** * Default name resolver. */ -class DefaultResolver extends \Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver +class DefaultResolver extends Base { /** * Get field name. diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php index 24412fa6baec8..77e1270f54fc2 100644 --- a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php +++ b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php @@ -150,7 +150,11 @@ private function getSuggestions(QueryInterface $query) } ksort($suggestions); $texts = array_unique(array_column($suggestions, 'text')); - $suggestions = array_slice(array_intersect_key(array_values($suggestions), $texts), 0, $searchSuggestionsCount); + $suggestions = array_slice( + array_intersect_key(array_values($suggestions), $texts), + 0, + $searchSuggestionsCount + ); } return $suggestions; @@ -186,7 +190,7 @@ private function initQuery($query) * Build Suggest on searchable fields. * * @param array $searchQuery - * @param int $searchSuggestionsCount + * @param int $searchSuggestionsCount * * @return array */ 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 837fef7a1935d..d85b8ba1fa225 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 @@ -208,3 +208,4 @@ Magento/InventoryGroupedProductIndexer/Indexer Magento/Customer/Model/FileUploaderDataResolver.php Magento/Customer/Model/Customer/DataProvider.php Magento/InventoryShippingAdminUi/Ui/DataProvider +Magento/Elasticsearch/Model/Client/Elasticsearch.php From b11652bb7c6377f3ba38407f6df25d8f35a47d28 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 28 Feb 2019 17:17:47 +0000 Subject: [PATCH 228/254] magento/magento2#21151: Fixed static tests --- app/code/Magento/Backup/Model/Db.php | 5 +++-- .../Backup/Model/ResourceModel/Table/GetListTables.php | 4 +++- .../Backup/Model/ResourceModel/View/GetListViews.php | 2 ++ .../Backup/Model/ResourceModel/View/GetViewsBackup.php | 10 ++++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Backup/Model/Db.php b/app/code/Magento/Backup/Model/Db.php index a385a476845db..971223cdecc4a 100644 --- a/app/code/Magento/Backup/Model/Db.php +++ b/app/code/Magento/Backup/Model/Db.php @@ -56,11 +56,12 @@ class Db implements \Magento\Framework\Backup\Db\BackupDbInterface private $getViewsBackup; /** - * @param \Magento\Backup\Model\ResourceModel\Db $resourceDb + * Db constructor. + * @param ResourceModel\Db $resourceDb * @param \Magento\Framework\App\ResourceConnection $resource + * @param Helper|null $helper * @param GetListTables|null $getListTables * @param GetViewsBackup|null $getViewsBackup - * @param Helper|null $helper */ public function __construct( \Magento\Backup\Model\ResourceModel\Db $resourceDb, diff --git a/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php index 5a9f96a50c35a..013fc729069f0 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php +++ b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php @@ -30,12 +30,14 @@ public function __construct(ResourceConnection $resource) } /** + * Get base tables + * * @return array */ public function execute() { return $this->resource->getConnection('backup')->fetchCol( -"SHOW FULL TABLES WHERE `Table_type` = ?", + "SHOW FULL TABLES WHERE `Table_type` = ?", self::TABLE_TYPE ); } diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php index 54f2a1a65d280..1c408dee8cac3 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php +++ b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php @@ -29,6 +29,8 @@ public function __construct(ResourceConnection $resource) } /** + * Get view tables + * * @return array */ public function execute() diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php b/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php index bda5ff58c0594..5a5f6a4d066e9 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php +++ b/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php @@ -44,6 +44,8 @@ public function __construct( } /** + * Backup + * * @param BackupInterface $backup */ public function execute(BackupInterface $backup) @@ -58,6 +60,8 @@ public function execute(BackupInterface $backup) } /** + * Get connection + * * @return AdapterInterface */ private function getConnection() @@ -70,6 +74,8 @@ private function getConnection() } /** + * Get show create view + * * @param string $viewName * @return string */ @@ -85,6 +91,8 @@ private function getShowCreateView($viewName) } /** + * Get view header + * * @param string $viewName * @return string */ @@ -95,6 +103,8 @@ public function getViewHeader($viewName) } /** + * Get drop view SQL + * * @param string $viewName * @return string */ From e5c5f41eb2495e5287fb338d7a7a805574e8c012 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Thu, 28 Feb 2019 16:50:41 -0600 Subject: [PATCH 229/254] GraphQL-37: [Cart Operations] Manage Cart Items -- Refactoring --- .../Model/Customer/CheckCustomerAccount.php | 12 +- .../Model/Cart/AssignBillingAddressToCart.php | 59 ++++ .../Cart/AssignShippingAddressToCart.php | 57 ++++ .../Model/Cart/ExtractDataFromCart.php | 68 ----- .../Model/Cart/GetCustomerAddress.php | 9 +- .../Model/Cart/QuoteAddressFactory.php | 67 +++++ .../Model/Cart/SetBillingAddressOnCart.php | 53 ++-- .../Model/Cart/SetShippingAddressOnCart.php | 44 ++- .../SetShippingAddressesOnCartInterface.php | 6 + .../Resolver/AddSimpleProductsToCart.php | 44 +-- .../Model/Resolver/AppliedCoupon.php | 34 +++ .../Model/Resolver/ApplyCouponToCart.php | 7 +- .../QuoteGraphQl/Model/Resolver/Cart.php | 17 +- .../QuoteGraphQl/Model/Resolver/CartItems.php | 48 +++ .../Model/Resolver/RemoveCouponFromCart.php | 7 +- ...mCartOutput.php => RemoveItemFromCart.php} | 41 +-- .../Resolver/SetBillingAddressOnCart.php | 26 +- .../Model/Resolver/SetPaymentMethodOnCart.php | 32 +- .../Resolver/SetShippingAddressesOnCart.php | 40 +-- .../Resolver/SetShippingMethodsOnCart.php | 7 +- .../Magento/QuoteGraphQl/etc/schema.graphqls | 21 +- .../Quote/AddSimpleProductToCartTest.php | 6 +- .../Magento/GraphQl/Quote/CouponTest.php | 4 +- .../GraphQl/Quote/Customer/GetCartTest.php | 3 - .../Quote/Customer/RemoveItemFromCartTest.php | 222 ++++++++++++++ .../Customer/SetPaymentMethodOnCartTest.php | 15 +- .../Quote/GetAvailableShippingMethodsTest.php | 2 - .../GraphQl/Quote/Guest/GetCartTest.php | 3 - .../Quote/Guest/RemoveItemFromCartTest.php | 166 ++++++++++ .../Guest/SetPaymentMethodOnCartTest.php | 5 - .../GraphQl/Quote/RemoveItemFromCartTest.php | 283 ------------------ 31 files changed, 817 insertions(+), 591 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php delete mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromCart.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/AppliedCoupon.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php rename app/code/Magento/QuoteGraphQl/Model/Resolver/{RemoveItemFromCartOutput.php => RemoveItemFromCart.php} (69%) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php delete mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php diff --git a/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php b/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php index 030fc47d19e81..b2f524c877fd6 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php +++ b/app/code/Magento/CustomerGraphQl/Model/Customer/CheckCustomerAccount.php @@ -11,9 +11,11 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Model\AuthenticationInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; /** @@ -58,6 +60,7 @@ public function __construct( * @param int|null $customerType * @return void * @throws GraphQlAuthorizationException + * @throws GraphQlInputException * @throws GraphQlNoSuchEntityException * @throws GraphQlAuthenticationException */ @@ -74,13 +77,20 @@ public function execute(?int $customerId, ?int $customerType): void __('Customer with id "%customer_id" does not exist.', ['customer_id' => $customerId]), $e ); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); } if (true === $this->authentication->isLocked($customerId)) { throw new GraphQlAuthenticationException(__('The account is locked.')); } - $confirmationStatus = $this->accountManagement->getConfirmationStatus($customerId); + try { + $confirmationStatus = $this->accountManagement->getConfirmationStatus($customerId); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } + if ($confirmationStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { throw new GraphQlAuthenticationException(__("This account isn't confirmed. Verify and try again.")); } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php new file mode 100644 index 0000000000000..370501e9c6e8e --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignBillingAddressToCart.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\BillingAddressManagementInterface; +use Magento\Quote\Model\Quote\Address as QuoteAddress; + +/** + * Set billing address for a specified shopping cart + */ +class AssignBillingAddressToCart +{ + /** + * @var BillingAddressManagementInterface + */ + private $billingAddressManagement; + + /** + * @param BillingAddressManagementInterface $billingAddressManagement + */ + public function __construct( + BillingAddressManagementInterface $billingAddressManagement + ) { + $this->billingAddressManagement = $billingAddressManagement; + } + + /** + * Assign billing address to cart + * + * @param CartInterface $cart + * @param QuoteAddress $billingAddress + * @param bool $useForShipping + * @throws GraphQlInputException + * @throws GraphQlNoSuchEntityException + */ + public function execute( + CartInterface $cart, + QuoteAddress $billingAddress, + bool $useForShipping + ): void { + try { + $this->billingAddressManagement->assign($cart->getId(), $billingAddress, $useForShipping); + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException(__($e->getMessage())); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php new file mode 100644 index 0000000000000..47f90edb04be8 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/AssignShippingAddressToCart.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Model\Quote\Address as QuoteAddress; +use Magento\Quote\Model\ShippingAddressManagementInterface; + +/** + * Assign shipping address to cart + */ +class AssignShippingAddressToCart +{ + /** + * @var ShippingAddressManagementInterface + */ + private $shippingAddressManagement; + + /** + * @param ShippingAddressManagementInterface $shippingAddressManagement + */ + public function __construct( + ShippingAddressManagementInterface $shippingAddressManagement + ) { + $this->shippingAddressManagement = $shippingAddressManagement; + } + + /** + * Assign shipping address to cart + * + * @param CartInterface $cart + * @param QuoteAddress $shippingAddress + * @throws GraphQlInputException + * @throws GraphQlNoSuchEntityException + */ + public function execute( + CartInterface $cart, + QuoteAddress $shippingAddress + ): void { + try { + $this->shippingAddressManagement->assign($cart->getId(), $shippingAddress); + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException(__($e->getMessage())); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromCart.php deleted file mode 100644 index 62ffdbd4b194f..0000000000000 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromCart.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\QuoteGraphQl\Model\Cart; - -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Quote\Model\Quote; -use Magento\Quote\Model\Quote\Item as QuoteItem; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; - -/** - * Extract data from cart - */ -class ExtractDataFromCart -{ - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedQuoteId; - - /** - * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId - */ - public function __construct( - QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedQuoteId - ) { - $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; - } - - /** - * Extract data from cart - * - * @param Quote $cart - * @return array - * @throws NoSuchEntityException - */ - public function execute(Quote $cart): array - { - $items = []; - - /** - * @var QuoteItem $cartItem - */ - foreach ($cart->getAllItems() as $cartItem) { - $productData = $cartItem->getProduct()->getData(); - $productData['model'] = $cartItem->getProduct(); - - $items[] = [ - 'id' => $cartItem->getItemId(), - 'qty' => $cartItem->getQty(), - 'product' => $productData, - 'model' => $cartItem, - ]; - } - - $appliedCoupon = $cart->getCouponCode(); - - return [ - 'cart_id' => $this->quoteIdToMaskedQuoteId->execute((int)$cart->getId()), - 'items' => $items, - 'applied_coupon' => $appliedCoupon ? ['code' => $appliedCoupon] : null - ]; - } -} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php index d3de86702b96c..93c888a1d0bd2 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCustomerAddress.php @@ -12,6 +12,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; /** @@ -33,14 +34,14 @@ public function __construct(AddressRepositoryInterface $addressRepository) } /** - * Get customer address. Throws exception if customer is not owner of address + * Get customer address * * @param int $addressId * @param int $customerId * @return AddressInterface - * @throws GraphQlAuthorizationException + * @throws GraphQlInputException * @throws GraphQlNoSuchEntityException - * @throws LocalizedException + * @throws GraphQlAuthorizationException */ public function execute(int $addressId, int $customerId): AddressInterface { @@ -50,6 +51,8 @@ public function execute(int $addressId, int $customerId): AddressInterface throw new GraphQlNoSuchEntityException( __('Could not find a address with ID "%address_id"', ['address_id' => $addressId]) ); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); } if ((int)$customerAddress->getCustomerId() !== $customerId) { diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php new file mode 100644 index 0000000000000..7dfea0836e8d6 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/QuoteAddressFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart; + +use Magento\Customer\Api\Data\AddressInterface as CustomerAddress; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Quote\Model\Quote\Address as QuoteAddress; +use Magento\Quote\Model\Quote\AddressFactory as BaseQuoteAddressFactory; + +/** + * Create QuoteAddress + */ +class QuoteAddressFactory +{ + /** + * @var BaseQuoteAddressFactory + */ + private $quoteAddressFactory; + + /** + * @param BaseQuoteAddressFactory $quoteAddressFactory + */ + public function __construct( + BaseQuoteAddressFactory $quoteAddressFactory + ) { + $this->quoteAddressFactory = $quoteAddressFactory; + } + + /** + * Create QuoteAddress based on input data + * + * @param array $addressInput + * @return QuoteAddress + */ + public function createBasedOnInputData(array $addressInput): QuoteAddress + { + $addressInput['country_id'] = $addressInput['country_code'] ?? ''; + + $quoteAddress = $this->quoteAddressFactory->create(); + $quoteAddress->addData($addressInput); + return $quoteAddress; + } + + /** + * Create QuoteAddress based on CustomerAddress + * + * @param CustomerAddress $customerAddress + * @return QuoteAddress + * @throws GraphQlInputException + */ + public function createBasedOnCustomerAddress(CustomerAddress $customerAddress): QuoteAddress + { + $quoteAddress = $this->quoteAddressFactory->create(); + try { + $quoteAddress->importCustomerAddressData($customerAddress); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } + return $quoteAddress; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php index 02aec5b6fbaf0..04b7bfcfe0e62 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetBillingAddressOnCart.php @@ -8,12 +8,12 @@ namespace Magento\QuoteGraphQl\Model\Cart; use Magento\CustomerGraphQl\Model\Customer\CheckCustomerAccount; +use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Model\Quote\Address; -use Magento\Quote\Api\BillingAddressManagementInterface; -use Magento\Customer\Api\AddressRepositoryInterface; /** * Set billing address for a specified shopping cart @@ -21,14 +21,9 @@ class SetBillingAddressOnCart { /** - * @var BillingAddressManagementInterface + * @var QuoteAddressFactory */ - private $billingAddressManagement; - - /** - * @var Address - */ - private $addressModel; + private $quoteAddressFactory; /** * @var CheckCustomerAccount @@ -41,24 +36,26 @@ class SetBillingAddressOnCart private $getCustomerAddress; /** - * @param BillingAddressManagementInterface $billingAddressManagement - * @param AddressRepositoryInterface $addressRepository - * @param Address $addressModel + * @var AssignBillingAddressToCart + */ + private $assignBillingAddressToCart; + + /** + * @param QuoteAddressFactory $quoteAddressFactory * @param CheckCustomerAccount $checkCustomerAccount * @param GetCustomerAddress $getCustomerAddress + * @param AssignBillingAddressToCart $assignBillingAddressToCart */ public function __construct( - BillingAddressManagementInterface $billingAddressManagement, - AddressRepositoryInterface $addressRepository, - Address $addressModel, + QuoteAddressFactory $quoteAddressFactory, CheckCustomerAccount $checkCustomerAccount, - GetCustomerAddress $getCustomerAddress + GetCustomerAddress $getCustomerAddress, + AssignBillingAddressToCart $assignBillingAddressToCart ) { - $this->billingAddressManagement = $billingAddressManagement; - $this->addressRepository = $addressRepository; - $this->addressModel = $addressModel; + $this->quoteAddressFactory = $quoteAddressFactory; $this->checkCustomerAccount = $checkCustomerAccount; $this->getCustomerAddress = $getCustomerAddress; + $this->assignBillingAddressToCart = $assignBillingAddressToCart; } /** @@ -69,38 +66,44 @@ public function __construct( * @param array $billingAddress * @return void * @throws GraphQlInputException + * @throws GraphQlAuthenticationException + * @throws GraphQlAuthorizationException + * @throws GraphQlNoSuchEntityException */ public function execute(ContextInterface $context, CartInterface $cart, array $billingAddress): void { $customerAddressId = $billingAddress['customer_address_id'] ?? null; $addressInput = $billingAddress['address'] ?? null; - $useForShipping = $billingAddress['use_for_shipping'] ?? false; + $useForShipping = isset($billingAddress['use_for_shipping']) + ? (bool)$billingAddress['use_for_shipping'] : false; 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.') ); } + $addresses = $cart->getAllShippingAddresses(); if ($useForShipping && count($addresses) > 1) { throw new GraphQlInputException( __('Using the "use_for_shipping" option with multishipping is not possible.') ); } + if (null === $customerAddressId) { - $addressInput['country_id'] = $addressInput['country_code'] ?? ''; - $billingAddress = $this->addressModel->addData($addressInput); + $billingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); } else { $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType()); $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$context->getUserId()); - $billingAddress = $this->addressModel->importCustomerAddressData($customerAddress); + $billingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress($customerAddress); } - $this->billingAddressManagement->assign($cart->getId(), $billingAddress, $useForShipping); + $this->assignBillingAddressToCart->execute($cart, $billingAddress, $useForShipping); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php index fc8eff4cfc13a..1a14424853491 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressOnCart.php @@ -11,8 +11,6 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Model\Quote\Address; -use Magento\Quote\Model\ShippingAddressManagementInterface; /** * Set single shipping address for a specified shopping cart @@ -20,14 +18,9 @@ class SetShippingAddressOnCart implements SetShippingAddressesOnCartInterface { /** - * @var ShippingAddressManagementInterface + * @var QuoteAddressFactory */ - private $shippingAddressManagement; - - /** - * @var Address - */ - private $addressModel; + private $quoteAddressFactory; /** * @var CheckCustomerAccount @@ -40,30 +33,30 @@ class SetShippingAddressOnCart implements SetShippingAddressesOnCartInterface private $getCustomerAddress; /** - * @param ShippingAddressManagementInterface $shippingAddressManagement - * @param Address $addressModel + * @var AssignShippingAddressToCart + */ + private $assignShippingAddressToCart; + + /** + * @param QuoteAddressFactory $quoteAddressFactory * @param CheckCustomerAccount $checkCustomerAccount * @param GetCustomerAddress $getCustomerAddress + * @param AssignShippingAddressToCart $assignShippingAddressToCart */ public function __construct( - ShippingAddressManagementInterface $shippingAddressManagement, - Address $addressModel, + QuoteAddressFactory $quoteAddressFactory, CheckCustomerAccount $checkCustomerAccount, - GetCustomerAddress $getCustomerAddress + GetCustomerAddress $getCustomerAddress, + AssignShippingAddressToCart $assignShippingAddressToCart ) { - $this->shippingAddressManagement = $shippingAddressManagement; - $this->addressModel = $addressModel; + $this->quoteAddressFactory = $quoteAddressFactory; $this->checkCustomerAccount = $checkCustomerAccount; $this->getCustomerAddress = $getCustomerAddress; + $this->assignShippingAddressToCart = $assignShippingAddressToCart; } /** * @inheritdoc - * - * @param ContextInterface $context - * @param CartInterface $cart - * @param array $shippingAddresses - * @throws GraphQlInputException */ public function execute(ContextInterface $context, CartInterface $cart, array $shippingAddresses): void { @@ -81,20 +74,21 @@ public function execute(ContextInterface $context, CartInterface $cart, array $s __('The shipping address must contain either "customer_address_id" or "address".') ); } + if ($customerAddressId && $addressInput) { throw new GraphQlInputException( __('The shipping address cannot contain "customer_address_id" and "address" at the same time.') ); } + if (null === $customerAddressId) { - $addressInput['country_id'] = $addressInput['country_code'] ?? ''; - $shippingAddress = $this->addressModel->addData($addressInput); + $shippingAddress = $this->quoteAddressFactory->createBasedOnInputData($addressInput); } else { $this->checkCustomerAccount->execute($context->getUserId(), $context->getUserType()); $customerAddress = $this->getCustomerAddress->execute((int)$customerAddressId, (int)$context->getUserId()); - $shippingAddress = $this->addressModel->importCustomerAddressData($customerAddress); + $shippingAddress = $this->quoteAddressFactory->createBasedOnCustomerAddress($customerAddress); } - $this->shippingAddressManagement->assign($cart->getId(), $shippingAddress); + $this->assignShippingAddressToCart->execute($cart, $shippingAddress); } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php index c5da3db75add7..81da47933e812 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/SetShippingAddressesOnCartInterface.php @@ -7,7 +7,10 @@ namespace Magento\QuoteGraphQl\Model\Cart; +use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Quote\Api\Data\CartInterface; @@ -27,6 +30,9 @@ interface SetShippingAddressesOnCartInterface * @param array $shippingAddresses * @return void * @throws GraphQlInputException + * @throws GraphQlAuthorizationException + * @throws GraphQlAuthenticationException + * @throws GraphQlNoSuchEntityException */ public function execute(ContextInterface $context, CartInterface $cart, array $shippingAddresses): void; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php index f4335b262c854..82ffd0970d672 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AddSimpleProductsToCart.php @@ -11,9 +11,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\Stdlib\ArrayManager; use Magento\QuoteGraphQl\Model\Cart\AddProductsToCart; -use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromCart; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; /** @@ -22,11 +20,6 @@ */ class AddSimpleProductsToCart implements ResolverInterface { - /** - * @var ArrayManager - */ - private $arrayManager; - /** * @var GetCartForUser */ @@ -38,26 +31,15 @@ class AddSimpleProductsToCart implements ResolverInterface private $addProductsToCart; /** - * @var ExtractDataFromCart - */ - private $extractDataFromCart; - - /** - * @param ArrayManager $arrayManager * @param GetCartForUser $getCartForUser * @param AddProductsToCart $addProductsToCart - * @param ExtractDataFromCart $extractDataFromCart */ public function __construct( - ArrayManager $arrayManager, GetCartForUser $getCartForUser, - AddProductsToCart $addProductsToCart, - ExtractDataFromCart $extractDataFromCart + AddProductsToCart $addProductsToCart ) { - $this->arrayManager = $arrayManager; $this->getCartForUser = $getCartForUser; $this->addProductsToCart = $addProductsToCart; - $this->extractDataFromCart = $extractDataFromCart; } /** @@ -65,25 +47,25 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - $cartHash = $this->arrayManager->get('input/cart_id', $args); - $cartItems = $this->arrayManager->get('input/cartItems', $args); - - if (!isset($cartHash)) { - throw new GraphQlInputException(__('Missing key "cart_id" in cart data')); + if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } + $maskedCartId = $args['input']['cart_id']; - if (!isset($cartItems) || !is_array($cartItems) || empty($cartItems)) { - throw new GraphQlInputException(__('Missing key "cartItems" in cart data')); + if (!isset($args['input']['cartItems']) || empty($args['input']['cartItems']) + || !is_array($args['input']['cartItems']) + ) { + throw new GraphQlInputException(__('Required parameter "cartItems" is missing')); } + $cartItems = $args['input']['cartItems']; - $currentUserId = $context->getUserId(); - $cart = $this->getCartForUser->execute((string)$cartHash, $currentUserId); - + $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); $this->addProductsToCart->execute($cart, $cartItems); - $cartData = $this->extractDataFromCart->execute($cart); return [ - 'cart' => $cartData, + 'cart' => [ + 'model' => $cart, + ], ]; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AppliedCoupon.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AppliedCoupon.php new file mode 100644 index 0000000000000..ca69b763929be --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AppliedCoupon.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * @inheritdoc + */ +class AppliedCoupon implements ResolverInterface +{ + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + $cart = $value['model']; + + $appliedCoupon = $cart->getCouponCode(); + + return $appliedCoupon ? ['code' => $appliedCoupon] : null; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php index ec59416d49371..a334e7482ca47 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ApplyCouponToCart.php @@ -80,9 +80,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new LocalizedException(__($exception->getMessage())); } - $data['cart']['applied_coupon'] = [ - 'code' => $couponCode, + return [ + 'cart' => [ + 'model' => $cart, + ], ]; - return $data; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/Cart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/Cart.php index 1849ba0803868..2df31841366a1 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/Cart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/Cart.php @@ -12,18 +12,12 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; -use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromCart; /** * @inheritdoc */ class Cart implements ResolverInterface { - /** - * @var ExtractDataFromCart - */ - private $extractDataFromCart; - /** * @var GetCartForUser */ @@ -31,14 +25,11 @@ class Cart implements ResolverInterface /** * @param GetCartForUser $getCartForUser - * @param ExtractDataFromCart $extractDataFromCart */ public function __construct( - GetCartForUser $getCartForUser, - ExtractDataFromCart $extractDataFromCart + GetCartForUser $getCartForUser ) { $this->getCartForUser = $getCartForUser; - $this->extractDataFromCart = $extractDataFromCart; } /** @@ -54,8 +45,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $currentUserId = $context->getUserId(); $cart = $this->getCartForUser->execute($maskedCartId, $currentUserId); - $data = $this->extractDataFromCart->execute($cart); - $data['model'] = $cart; - return $data; + return [ + 'model' => $cart, + ]; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php new file mode 100644 index 0000000000000..da6619d15a489 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItems.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Resolver; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Quote\Model\Quote\Item as QuoteItem; + +/** + * @inheritdoc + */ +class CartItems implements ResolverInterface +{ + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + $cart = $value['model']; + + $itemsData = []; + foreach ($cart->getAllItems() as $cartItem) { + /** + * @var QuoteItem $cartItem + */ + $productData = $cartItem->getProduct()->getData(); + $productData['model'] = $cartItem->getProduct(); + + $itemsData[] = [ + 'id' => $cartItem->getItemId(), + 'qty' => $cartItem->getQty(), + 'product' => $productData, + 'model' => $cartItem, + ]; + } + return $itemsData; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php index c21d869ddac7d..730c927c32a0c 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveCouponFromCart.php @@ -67,9 +67,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new LocalizedException(__($exception->getMessage())); } - $data['cart']['applied_coupon'] = [ - 'code' => '', + return [ + 'cart' => [ + 'model' => $cart, + ], ]; - return $data; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCartOutput.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCart.php similarity index 69% rename from app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCartOutput.php rename to app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCart.php index 658f670fd6825..1838f17369ffc 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCartOutput.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/RemoveItemFromCart.php @@ -7,6 +7,7 @@ namespace Magento\QuoteGraphQl\Model\Resolver; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; @@ -14,13 +15,12 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Quote\Api\GuestCartItemRepositoryInterface; -use Magento\Quote\Api\GuestCartRepositoryInterface; -use Magento\QuoteGraphQl\Model\Cart\ExtractDataFromCart; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; /** * @inheritdoc */ -class RemoveItemFromCartOutput implements ResolverInterface +class RemoveItemFromCart implements ResolverInterface { /** * @var GuestCartItemRepositoryInterface @@ -28,46 +28,51 @@ class RemoveItemFromCartOutput implements ResolverInterface private $guestCartItemRepository; /** - * @var GuestCartRepositoryInterface + * @var GetCartForUser */ - private $guestCartRepository; + private $getCartForUser; /** - * @var ExtractDataFromCart + * @param GuestCartItemRepositoryInterface $guestCartItemRepository + * @param GetCartForUser $getCartForUser */ - private $extractDataFromCart; - public function __construct( GuestCartItemRepositoryInterface $guestCartItemRepository, - GuestCartRepositoryInterface $guestCartRepository, - ExtractDataFromCart $extractDataFromCart + GetCartForUser $getCartForUser ) { $this->guestCartItemRepository = $guestCartItemRepository; - $this->guestCartRepository = $guestCartRepository; - $this->extractDataFromCart = $extractDataFromCart; + $this->getCartForUser = $getCartForUser; } + /** + * @inheritdoc + */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!isset($args['input']['cart_id'])) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } + $maskedCartId = $args['input']['cart_id']; + if (!isset($args['input']['cart_item_id'])) { throw new GraphQlInputException(__('Required parameter "cart_item_id" is missing')); } - $maskedCartId = $args['input']['cart_id']; $itemId = $args['input']['cart_item_id']; + $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); + try { $this->guestCartItemRepository->deleteById($maskedCartId, $itemId); } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException(__($e->getMessage())); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); } - $cart = $this->guestCartRepository->get($maskedCartId); - - $cartData = $this->extractDataFromCart->execute($cart); - - return ['cart' => array_merge(['cart_id' => $maskedCartId], $cartData)]; + return [ + 'cart' => [ + 'model' => $cart, + ], + ]; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetBillingAddressOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetBillingAddressOnCart.php index 01a35f4b4152f..f7c9a4b0697b8 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetBillingAddressOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetBillingAddressOnCart.php @@ -11,13 +11,10 @@ use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\Stdlib\ArrayManager; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; use Magento\QuoteGraphQl\Model\Cart\SetBillingAddressOnCart as SetBillingAddressOnCartModel; /** - * Class SetBillingAddressOnCart - * * Mutation resolver for setting billing address for shopping cart */ class SetBillingAddressOnCart implements ResolverInterface @@ -27,11 +24,6 @@ class SetBillingAddressOnCart implements ResolverInterface */ private $getCartForUser; - /** - * @var ArrayManager - */ - private $arrayManager; - /** * @var SetBillingAddressOnCartModel */ @@ -39,16 +31,13 @@ class SetBillingAddressOnCart implements ResolverInterface /** * @param GetCartForUser $getCartForUser - * @param ArrayManager $arrayManager * @param SetBillingAddressOnCartModel $setBillingAddressOnCart */ public function __construct( GetCartForUser $getCartForUser, - ArrayManager $arrayManager, SetBillingAddressOnCartModel $setBillingAddressOnCart ) { $this->getCartForUser = $getCartForUser; - $this->arrayManager = $arrayManager; $this->setBillingAddressOnCart = $setBillingAddressOnCart; } @@ -57,26 +46,23 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - $billingAddress = $this->arrayManager->get('input/billing_address', $args); - $maskedCartId = $this->arrayManager->get('input/cart_id', $args); - - if (!$maskedCartId) { + if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } - if (!$billingAddress) { + $maskedCartId = $args['input']['cart_id']; + + if (!isset($args['input']['billing_address']) || empty($args['input']['billing_address'])) { throw new GraphQlInputException(__('Required parameter "billing_address" is missing')); } + $billingAddress = $args['input']['billing_address']; - $maskedCartId = $args['input']['cart_id']; $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); - $this->setBillingAddressOnCart->execute($context, $cart, $billingAddress); return [ 'cart' => [ - 'cart_id' => $maskedCartId, 'model' => $cart, - ] + ], ]; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php index 78a841a9cb614..ffd1bf37f4771 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php @@ -8,11 +8,12 @@ namespace Magento\QuoteGraphQl\Model\Resolver; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Config\Element\Field; 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; -use Magento\Framework\Stdlib\ArrayManager; use Magento\Quote\Api\Data\PaymentInterface; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; use Magento\Quote\Api\Data\PaymentInterfaceFactory; @@ -28,11 +29,6 @@ class SetPaymentMethodOnCart implements ResolverInterface */ private $getCartForUser; - /** - * @var ArrayManager - */ - private $arrayManager; - /** * @var PaymentMethodManagementInterface */ @@ -45,18 +41,15 @@ class SetPaymentMethodOnCart implements ResolverInterface /** * @param GetCartForUser $getCartForUser - * @param ArrayManager $arrayManager * @param PaymentMethodManagementInterface $paymentMethodManagement * @param PaymentInterfaceFactory $paymentFactory */ public function __construct( GetCartForUser $getCartForUser, - ArrayManager $arrayManager, PaymentMethodManagementInterface $paymentMethodManagement, PaymentInterfaceFactory $paymentFactory ) { $this->getCartForUser = $getCartForUser; - $this->arrayManager = $arrayManager; $this->paymentMethodManagement = $paymentMethodManagement; $this->paymentFactory = $paymentFactory; } @@ -66,22 +59,20 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - $maskedCartId = (string)$this->arrayManager->get('input/cart_id', $args); - if (!$maskedCartId) { + if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } + $maskedCartId = $args['input']['cart_id']; - $paymentMethod = $this->arrayManager->get('input/payment_method', $args); - if (!$paymentMethod) { + if (!isset($args['input']['payment_method']['code']) || empty($args['input']['payment_method']['code'])) { throw new GraphQlInputException(__('Required parameter "payment_method" is missing')); } + $paymentMethodCode = $args['input']['payment_method']['code']; - $paymentMethodCode = (string) $this->arrayManager->get('input/payment_method/code', $args); - if (!$paymentMethodCode) { - throw new GraphQlInputException(__('Required parameter payment "code" is missing')); - } - - $poNumber = $this->arrayManager->get('input/payment_method/purchase_order_number', $args); + $poNumber = isset($args['input']['payment_method']['purchase_order_number']) + && empty($args['input']['payment_method']['purchase_order_number']) + ? $args['input']['payment_method']['purchase_order_number'] + : null; $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); $payment = $this->paymentFactory->create([ @@ -94,13 +85,14 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value try { $this->paymentMethodManagement->set($cart->getId(), $payment); + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException(__($e->getMessage())); } catch (LocalizedException $e) { throw new GraphQlInputException(__($e->getMessage())); } return [ 'cart' => [ - 'cart_id' => $maskedCartId, 'model' => $cart, ], ]; diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php index a55e2971e0ef7..c3e1d371fe6a4 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingAddressesOnCart.php @@ -7,58 +7,37 @@ namespace Magento\QuoteGraphQl\Model\Resolver; -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\Framework\Stdlib\ArrayManager; -use Magento\Quote\Model\ShippingAddressManagementInterface; use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; use Magento\QuoteGraphQl\Model\Cart\SetShippingAddressesOnCartInterface; /** - * Class SetShippingAddressesOnCart - * * Mutation resolver for setting shipping addresses for shopping cart */ class SetShippingAddressesOnCart implements ResolverInterface { - /** - * @var ShippingAddressManagementInterface - */ - private $shippingAddressManagement; - /** * @var GetCartForUser */ private $getCartForUser; - /** - * @var ArrayManager - */ - private $arrayManager; - /** * @var SetShippingAddressesOnCartInterface */ private $setShippingAddressesOnCart; /** - * @param ShippingAddressManagementInterface $shippingAddressManagement * @param GetCartForUser $getCartForUser - * @param ArrayManager $arrayManager * @param SetShippingAddressesOnCartInterface $setShippingAddressesOnCart */ public function __construct( - ShippingAddressManagementInterface $shippingAddressManagement, GetCartForUser $getCartForUser, - ArrayManager $arrayManager, SetShippingAddressesOnCartInterface $setShippingAddressesOnCart ) { - $this->shippingAddressManagement = $shippingAddressManagement; $this->getCartForUser = $getCartForUser; - $this->arrayManager = $arrayManager; $this->setShippingAddressesOnCart = $setShippingAddressesOnCart; } @@ -67,30 +46,23 @@ public function __construct( */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { - $shippingAddresses = $this->arrayManager->get('input/shipping_addresses', $args); - $maskedCartId = (string)$this->arrayManager->get('input/cart_id', $args); - - if (!$maskedCartId) { + if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) { throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); } + $maskedCartId = $args['input']['cart_id']; - if (!$shippingAddresses) { + if (!isset($args['input']['shipping_addresses']) || empty($args['input']['shipping_addresses'])) { throw new GraphQlInputException(__('Required parameter "shipping_addresses" is missing')); } + $shippingAddresses = $args['input']['shipping_addresses']; $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); - - try { - $this->setShippingAddressesOnCart->execute($context, $cart, $shippingAddresses); - } catch (LocalizedException $e) { - throw new GraphQlInputException(__($e->getMessage())); - } + $this->setShippingAddressesOnCart->execute($context, $cart, $shippingAddresses); return [ 'cart' => [ - 'cart_id' => $maskedCartId, 'model' => $cart, - ] + ], ]; } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php index 67947e928796c..3950434301a8f 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetShippingMethodsOnCart.php @@ -16,8 +16,6 @@ use Magento\QuoteGraphQl\Model\Cart\SetShippingMethodOnCart; /** - * Class SetShippingMethodsOnCart - * * Mutation resolver for setting shipping methods for shopping cart */ class SetShippingMethodsOnCart implements ResolverInterface @@ -91,9 +89,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return [ 'cart' => [ - 'cart_id' => $maskedCartId, - 'model' => $cart - ] + 'model' => $cart, + ], ]; } } diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 93d5842a4b143..e4ced2351572c 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -11,7 +11,7 @@ type Mutation { addVirtualProductsToCart(input: AddVirtualProductsToCartInput): AddVirtualProductsToCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AddSimpleProductsToCart") applyCouponToCart(input: ApplyCouponToCartInput): ApplyCouponToCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ApplyCouponToCart") removeCouponFromCart(input: RemoveCouponFromCartInput): RemoveCouponFromCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveCouponFromCart") - removeItemFromCart(input: RemoveItemFromCartInput): RemoveItemFromCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveItemFromCartOutput") + removeItemFromCart(input: RemoveItemFromCartInput): RemoveItemFromCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\RemoveItemFromCart") setShippingAddressesOnCart(input: SetShippingAddressesOnCartInput): SetShippingAddressesOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingAddressesOnCart") setBillingAddressOnCart(input: SetBillingAddressOnCartInput): SetBillingAddressOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetBillingAddressOnCart") setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart") @@ -53,6 +53,11 @@ input ApplyCouponToCartInput { coupon_code: String! } +input RemoveItemFromCartInput { + cart_id: String! + cart_item_id: Int! +} + input SetShippingAddressesOnCartInput { cart_id: String! shipping_addresses: [ShippingAddressInput!]! @@ -139,12 +144,11 @@ type ApplyCouponToCartOutput { } type Cart { - cart_id: String - items: [CartItemInterface] - applied_coupon: AppliedCoupon + items: [CartItemInterface] @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\CartItems") + applied_coupon: AppliedCoupon @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\AppliedCoupon") shipping_addresses: [CartAddress]! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\ShippingAddresses") billing_address: CartAddress! @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\BillingAddress") - available_payment_methods : [AvailablePaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods") + available_payment_methods: [AvailablePaymentMethod] @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\AvailablePaymentMethods") @doc(description: "Available payment methods") selected_payment_method: SelectedPaymentMethod @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SelectedPaymentMethod") } @@ -168,7 +172,7 @@ type CartAddress { } type CartItemQuantity { - cart_item_id: String! + cart_item_id: Int! quantity: Float! } @@ -237,11 +241,6 @@ type AddVirtualProductsToCartOutput { cart: Cart! } -input RemoveItemFromCartInput { - cart_id: String! - cart_item_id: String! -} - type RemoveItemFromCartOutput { cart: Cart! } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php index 4cbc614e1b8dc..d5bdb942e9b2c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddSimpleProductToCartTest.php @@ -63,7 +63,7 @@ public function testAddProductIfQuantityIsNotAvailable() mutation { addSimpleProductsToCart( input: { - cart_id: "{$maskedQuoteId}", + cart_id: "{$maskedQuoteId}" cartItems: [ { data: { @@ -75,7 +75,9 @@ public function testAddProductIfQuantityIsNotAvailable() } ) { cart { - cart_id + items { + qty + } } } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CouponTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CouponTest.php index 1f8ad06a9f8ed..54fdc3ac0f360 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CouponTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/CouponTest.php @@ -58,7 +58,7 @@ public function testApplyCouponToGuestCartWithItems() $query = $this->prepareAddCouponRequestQuery($maskedQuoteId, $couponCode); $response = $this->graphQlQuery($query); - self::assertArrayHasKey("applyCouponToCart", $response); + self::assertArrayHasKey('applyCouponToCart', $response); self::assertEquals($couponCode, $response['applyCouponToCart']['cart']['applied_coupon']['code']); } @@ -154,7 +154,7 @@ public function testRemoveCoupon() $response = $this->graphQlQuery($query); self::assertArrayHasKey('removeCouponFromCart', $response); - self::assertSame('', $response['removeCouponFromCart']['cart']['applied_coupon']['code']); + self::assertNull($response['removeCouponFromCart']['cart']['applied_coupon']['code']); } /** 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 8c1fcce7fb550..85a3fc8620c68 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 @@ -59,8 +59,6 @@ public function testGetCart() $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); self::assertArrayHasKey('cart', $response); - self::assertEquals($maskedQuoteId, $response['cart']['cart_id']); - self::assertArrayHasKey('items', $response['cart']); self::assertCount(2, $response['cart']['items']); @@ -126,7 +124,6 @@ private function getCartQuery( return <<<QUERY { cart(cart_id: "$maskedQuoteId") { - cart_id items { id qty 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 new file mode 100644 index 0000000000000..a351a2188a664 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/RemoveItemFromCartTest.php @@ -0,0 +1,222 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Customer; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for removeItemFromCartTest mutation + */ +class RemoveItemFromCartTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testRemoveItemFromCart() + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, 'test_order_1', 'reserved_order_id'); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); + $itemId = (int)$quote->getItemByProduct($this->productRepository->get('simple'))->getId(); + + $query = $this->prepareMutationQuery($maskedQuoteId, $itemId); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + $this->assertArrayHasKey('removeItemFromCart', $response); + $this->assertArrayHasKey('cart', $response['removeItemFromCart']); + $this->assertCount(0, $response['removeItemFromCart']['cart']['items']); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @expectedException \Exception + * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" + */ + public function testRemoveItemFromNonExistentCart() + { + $query = $this->prepareMutationQuery('non_existent_masked_id', 1); + + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testRemoveNotExistentItem() + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, 'test_order_1', 'reserved_order_id'); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); + $notExistentItemId = 999; + + $this->expectExceptionMessage("Cart doesn't contain the {$notExistentItemId} item."); + + $query = $this->prepareMutationQuery($maskedQuoteId, $notExistentItemId); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + */ + public function testRemoveItemIfItemIsNotBelongToCart() + { + $firstQuote = $this->quoteFactory->create(); + $this->quoteResource->load($firstQuote, 'test_order_1', 'reserved_order_id'); + $firstQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); + + $secondQuote = $this->quoteFactory->create(); + $this->quoteResource->load( + $secondQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); + $secondQuote->setCustomerId(1); + $this->quoteResource->save($secondQuote); + $secondQuoteItemId = (int)$secondQuote + ->getItemByProduct($this->productRepository->get('virtual-product')) + ->getId(); + + $this->expectExceptionMessage("Cart doesn't contain the {$secondQuoteItemId} item."); + + $query = $this->prepareMutationQuery($firstQuoteMaskedId, $secondQuoteItemId); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + */ + public function testRemoveItemFromGuestCart() + { + $guestQuote = $this->quoteFactory->create(); + $this->quoteResource->load( + $guestQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); + $guestQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$guestQuote->getId()); + $guestQuoteItemId = (int)$guestQuote + ->getItemByProduct($this->productRepository->get('virtual-product')) + ->getId(); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$guestQuoteMaskedId\"" + ); + + $query = $this->prepareMutationQuery($guestQuoteMaskedId, $guestQuoteItemId); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/three_customers.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + */ + public function testRemoveItemFromAnotherCustomerCart() + { + $anotherCustomerQuote = $this->quoteFactory->create(); + $this->quoteResource->load( + $anotherCustomerQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); + $anotherCustomerQuote->setCustomerId(2); + $this->quoteResource->save($anotherCustomerQuote); + + $anotherCustomerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$anotherCustomerQuote->getId()); + $anotherCustomerQuoteItemId = (int)$anotherCustomerQuote + ->getItemByProduct($this->productRepository->get('virtual-product')) + ->getId(); + + $this->expectExceptionMessage( + "The current user cannot perform operations on cart \"$anotherCustomerQuoteMaskedId\"" + ); + + $query = $this->prepareMutationQuery($anotherCustomerQuoteMaskedId, $anotherCustomerQuoteItemId); + $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + } + + /** + * @param string $maskedQuoteId + * @param int $itemId + * @return string + */ + private function prepareMutationQuery(string $maskedQuoteId, int $itemId): string + { + return <<<QUERY +mutation { + removeItemFromCart( + input: { + cart_id: "{$maskedQuoteId}" + cart_item_id: {$itemId} + } + ) { + cart { + items { + qty + } + } + } +} +QUERY; + } + + /** + * @param string $username + * @param string $password + * @return array + */ + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + return $headerMap; + } +} 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 8856b2ab44c22..315cbc86b2de1 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 @@ -65,7 +65,6 @@ public function testSetPaymentWithVirtualProduct() self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); } @@ -83,7 +82,6 @@ public function testSetPaymentWithSimpleProduct() self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); } @@ -154,8 +152,6 @@ public function testSetPaymentMethodIfCustomerIsNotOwnerOfCart() } /** - * Generates query for setting the specified shipping method on cart - * * @param string $maskedQuoteId * @param string $methodCode * @return string @@ -166,16 +162,13 @@ private function prepareMutationQuery( ) : string { return <<<QUERY mutation { - setPaymentMethodOnCart(input: - { - cart_id: "$maskedQuoteId", + setPaymentMethodOnCart(input: { + cart_id: "$maskedQuoteId" payment_method: { code: "$methodCode" - } - }) { - + } + }) { cart { - cart_id, selected_payment_method { code } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailableShippingMethodsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailableShippingMethodsTest.php index 7b72afa157018..1a0ccbd198c37 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailableShippingMethodsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetAvailableShippingMethodsTest.php @@ -63,7 +63,6 @@ public function testGetAvailableShippingMethods() $query = <<<QUERY query { cart (cart_id: "{$maskedQuoteId}") { - cart_id shipping_addresses { available_shipping_methods { amount @@ -87,7 +86,6 @@ public function testGetAvailableShippingMethods() $this->getCustomerAuthHeaders('customer@example.com', 'password') ); self::assertArrayHasKey('cart', $response); - self::assertEquals($maskedQuoteId, $response['cart']['cart_id']); self::assertArrayHasKey('shipping_addresses', $response['cart']); self::assertCount(1, $response['cart']['shipping_addresses']); self::assertArrayHasKey('available_shipping_methods', $response['cart']['shipping_addresses'][0]); 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 42b5cbd06b9fc..b1d5e475c793e 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 @@ -59,8 +59,6 @@ public function testGetCart() $response = $this->graphQlQuery($query); self::assertArrayHasKey('cart', $response); - self::assertEquals($maskedQuoteId, $response['cart']['cart_id']); - self::assertArrayHasKey('items', $response['cart']); self::assertCount(2, $response['cart']['items']); @@ -109,7 +107,6 @@ private function getCartQuery( return <<<QUERY { cart(cart_id: "$maskedQuoteId") { - cart_id items { id qty 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 new file mode 100644 index 0000000000000..a6b8f05fc0834 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/RemoveItemFromCartTest.php @@ -0,0 +1,166 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote\Guest; + +use Magento\Quote\Model\QuoteFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test for removeItemFromCartTest mutation + */ +class RemoveItemFromCartTest extends GraphQlAbstract +{ + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->quoteResource = $objectManager->get(QuoteResource::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class); + $this->productRepository = $objectManager->get(ProductRepositoryInterface::class); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testRemoveItemFromCart() + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); + $itemId = (int)$quote->getItemByProduct($this->productRepository->get('simple'))->getId(); + + $query = $this->prepareMutationQuery($maskedQuoteId, $itemId); + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('removeItemFromCart', $response); + $this->assertArrayHasKey('cart', $response['removeItemFromCart']); + $this->assertCount(0, $response['removeItemFromCart']['cart']['items']); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Could not find a cart with ID "non_existent_masked_id" + */ + public function testRemoveItemFromNonExistentCart() + { + $query = $this->prepareMutationQuery('non_existent_masked_id', 1); + + $this->graphQlQuery($query); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + */ + public function testRemoveNotExistentItem() + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); + $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$quote->getId()); + $notExistentItemId = 999; + + $this->expectExceptionMessage("Cart doesn't contain the {$notExistentItemId} item."); + + $query = $this->prepareMutationQuery($maskedQuoteId, $notExistentItemId); + $this->graphQlQuery($query); + } + + /** + * Test mutation is only able to remove quote item belonging to the requested cart + * + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php + */ + public function testRemoveItemIfItemIsNotBelongToCart() + { + $firstQuote = $this->quoteFactory->create(); + $this->quoteResource->load($firstQuote, 'test_order_with_simple_product_without_address', 'reserved_order_id'); + $firstQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$firstQuote->getId()); + + $secondQuote = $this->quoteFactory->create(); + $this->quoteResource->load( + $secondQuote, + 'test_order_with_virtual_product_without_address', + 'reserved_order_id' + ); + $secondQuoteItemId = (int)$secondQuote + ->getItemByProduct($this->productRepository->get('virtual-product')) + ->getId(); + + $this->expectExceptionMessage("Cart doesn't contain the {$secondQuoteItemId} item."); + + $query = $this->prepareMutationQuery($firstQuoteMaskedId, $secondQuoteItemId); + $this->graphQlQuery($query); + } + + /** + * Test mutation is only able to remove quote item belonging to the requested cart + * + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + */ + public function testRemoveItemFromCustomerCart() + { + $customerQuote = $this->quoteFactory->create(); + $this->quoteResource->load($customerQuote, 'test_order_1', 'reserved_order_id'); + $customerQuoteMaskedId = $this->quoteIdToMaskedId->execute((int)$customerQuote->getId()); + $customerQuoteItemId = (int)$customerQuote->getItemByProduct($this->productRepository->get('simple'))->getId(); + + $this->expectExceptionMessage("The current user cannot perform operations on cart \"$customerQuoteMaskedId\""); + + $query = $this->prepareMutationQuery($customerQuoteMaskedId, $customerQuoteItemId); + $this->graphQlQuery($query); + } + + /** + * @param string $maskedQuoteId + * @param int $itemId + * @return string + */ + private function prepareMutationQuery(string $maskedQuoteId, int $itemId): string + { + return <<<QUERY +mutation { + removeItemFromCart( + input: { + cart_id: "{$maskedQuoteId}" + cart_item_id: {$itemId} + } + ) { + cart { + items { + qty + } + } + } +} +QUERY; + } +} 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 8286f97148953..7484b2af7569d 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 @@ -59,7 +59,6 @@ public function testSetPaymentWithVirtualProduct() self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); } @@ -78,7 +77,6 @@ public function testSetPaymentWithSimpleProduct() self::assertArrayHasKey('setPaymentMethodOnCart', $response); self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']); - self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']); self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']); self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']); } @@ -129,8 +127,6 @@ public function testSetPaymentMethodToCustomerCart() } /** - * Generates query for setting the specified shipping method on cart - * * @param string $maskedQuoteId * @param string $methodCode * @return string @@ -150,7 +146,6 @@ private function prepareMutationQuery( }) { cart { - cart_id, selected_payment_method { code } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php deleted file mode 100644 index d7d3d5c8b9e92..0000000000000 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/RemoveItemFromCartTest.php +++ /dev/null @@ -1,283 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\GraphQl\Quote; - -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Integration\Api\CustomerTokenServiceInterface; -use Magento\Framework\ObjectManagerInterface; -use Magento\Quote\Api\GuestCartRepositoryInterface; -use Magento\Quote\Model\Quote; -use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; -use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\GraphQlAbstract; -use Magento\TestFramework\ObjectManager; - -/** - * Test for empty cart creation mutation - */ -class RemoveItemFromCartTest extends GraphQlAbstract -{ - /** - * @var ObjectManagerInterface - */ - private $objectManager; - - /** - * @var QuoteResource - */ - private $quoteResource; - - /** - * @var Quote - */ - private $quote; - - /** - * @var QuoteIdToMaskedQuoteIdInterface - */ - private $quoteIdToMaskedId; - - /** - * @var ProductRepositoryInterface - */ - private $productRepository; - - /** - * @var GuestCartRepositoryInterface - */ - private $guestCartRepository; - - protected function setUp() - { - $this->objectManager = Bootstrap::getObjectManager(); - $this->quoteResource = $this->objectManager->create(QuoteResource::class); - $this->quote = $this->objectManager->create(Quote::class); - $this->quoteIdToMaskedId = $this->objectManager->create(QuoteIdToMaskedQuoteIdInterface::class); - $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); - $this->guestCartRepository = $this->objectManager->create(GuestCartRepositoryInterface::class); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - */ - public function testGuestRemoveItemFromCart() - { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $itemId = $this->quote->getItemByProduct($this->productRepository->get('simple'))->getId(); - - $query = <<<QUERY -mutation { - removeItemFromCart( - input: { - cart_id: "$maskedQuoteId" - cart_item_id: "$itemId" - } - ) { - cart { - cart_id - items { - id - qty - } - } - } -} -QUERY; - $response = $this->graphQlQuery($query); - - $this->assertArrayHasKey('removeItemFromCart', $response); - $this->assertArrayHasKey('cart', $response['removeItemFromCart']); - - $responseCart = $response['removeItemFromCart']['cart']; - - $this->assertCount(0, $responseCart['items']); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/Customer/_files/customer.php - */ - public function testRemoveItemFromCart() - { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $itemId = $this->quote->getItemByProduct($this->productRepository->get('simple'))->getId(); - - $this->quote->setCustomerId(1); - $this->quoteResource->save($this->quote); - - $headerMap = $this->getHeaderMap(); - - $query = <<<QUERY -mutation { - removeItemFromCart( - input: { - cart_id: "$maskedQuoteId" - cart_item_id: "$itemId" - } - ) { - cart { - cart_id - items { - id - qty - } - } - } -} -QUERY; - - $response = $this->graphQlQuery($query, [], '', $headerMap); - - $this->assertArrayHasKey('removeItemFromCart', $response); - $this->assertArrayHasKey('cart', $response['removeItemFromCart']); - - $responseCart = $response['removeItemFromCart']['cart']; - - $this->assertCount(0, $responseCart['items']); - } - - public function testRemoveItemFromCartNoSuchCartIdException() - { - $maskedCartId = 'nada'; - - $this->expectExceptionMessage('No such entity with cartId'); - - $query = <<<QUERY -mutation { - removeItemFromCart( - input: { - cart_id: "$maskedCartId" - cart_item_id: "nononono" - } - ) { - cart { - cart_id - items { - id - qty - } - } - } -} -QUERY; - $this->graphQlQuery($query); - } - - /** - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - */ - public function testRemoveItemFromCartNoSuchCartItem() - { - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $itemId = 'nononono'; - - $this->expectExceptionMessage(sprintf('Cart doesn\'t contain the %s item.', $itemId)); - - $query = <<<QUERY -mutation { - removeItemFromCart( - input: { - cart_id: "$maskedQuoteId" - cart_item_id: "$itemId" - } - ) { - cart { - cart_id - items { - id - qty - } - } - } -} -QUERY; - $this->graphQlQuery($query); - } - - /** - * Test mutation is only able to remove quote item belonging to the requested cart - * - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php - * @magentoApiDataFixture Magento/Checkout/_files/quote_with_virtual_product_saved.php - * @expectedException \Exception - * @expectedExceptionMessage Could not find cart item with id - */ - public function testRemoveItemFromDifferentQuote() - { - /** @var Quote $secondQuote */ - $secondQuote = $this->objectManager->create(Quote::class); - $this->quoteResource->load( - $secondQuote, - 'test_order_with_virtual_product_without_address', - 'reserved_order_id' - ); - $secondQuoteItem = $secondQuote->getItemByProduct($this->productRepository->get('virtual-product')); - - $this->quoteResource->load( - $this->quote, - 'test_order_with_simple_product_without_address', - 'reserved_order_id' - ); - $maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId()); - $itemId = $secondQuoteItem->getItemId(); - - $this->expectExceptionMessage(sprintf('Cart doesn\'t contain the %s item.', $itemId)); - - $query = <<<QUERY -mutation { - removeItemFromCart( - input: { - cart_id: "$maskedQuoteId" - cart_item_id: "$itemId" - } - ) { - cart { - cart_id - items { - id - qty - } - } - } -} -QUERY; - - $this->graphQlQuery($query); - } - - /** - * @param string $username - * @return array - */ - private function getHeaderMap(string $username = 'customer@example.com'): array - { - $password = 'password'; - /** @var CustomerTokenServiceInterface $customerTokenService */ - $customerTokenService = ObjectManager::getInstance() - ->get(CustomerTokenServiceInterface::class); - $customerToken = $customerTokenService->createCustomerAccessToken($username, $password); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - return $headerMap; - } -} From 445c02df1fe7f8683ed9587e4c041e09a939c2a1 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Thu, 28 Feb 2019 17:32:08 -0600 Subject: [PATCH 230/254] GraphQL-37: [Cart Operations] Manage Cart Items -- Refactoring --- .../testsuite/Magento/GraphQl/Quote/Customer/GetCartTest.php | 2 +- .../GraphQl/Quote/Customer/SetBillingAddressOnCartTest.php | 2 +- .../GraphQl/Quote/Customer/SetPaymentMethodOnCartTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 85a3fc8620c68..fe9b7b3c49a7c 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 @@ -90,7 +90,7 @@ public function testGetGuestCart() * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php */ - public function testGetCartIfCustomerIsNotOwnerOfCart() + public function testGetAnotherCustomerCart() { $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_item_with_items'); $query = $this->getCartQuery($maskedQuoteId); 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 2e0b57f96fe3a..96f32d781267b 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 @@ -339,7 +339,7 @@ public function testSetBillingAddressToGuestCart() * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php */ - public function testSetBillingAddressIfCustomerIsNotOwnerOfCart() + public function testSetBillingAddressToAnotherCustomerCart() { $maskedQuoteId = $this->assignQuoteToCustomer('test_order_with_simple_product_without_address', 2); 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 315cbc86b2de1..bbc77b6c39740 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 @@ -137,7 +137,7 @@ public function testSetPaymentMethodToGuestCart() * @magentoApiDataFixture Magento/Customer/_files/three_customers.php * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php */ - public function testSetPaymentMethodIfCustomerIsNotOwnerOfCart() + public function testSetPaymentMethodToAnotherCustomerCart() { $methodCode = Checkmo::PAYMENT_METHOD_CHECKMO_CODE; $maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1'); From b48d4326397e81246fc8489dfecc6e8a34fc2287 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 28 Feb 2019 21:17:59 -0600 Subject: [PATCH 231/254] magento-engcom/magento2ce#2639: Minor coding practices improvements --- app/code/Magento/Backup/Model/Db.php | 16 +++++------ .../ResourceModel/Table/GetListTables.php | 6 ++-- ...tViewsBackup.php => CreateViewsBackup.php} | 28 +++++++++---------- .../Model/ResourceModel/View/GetListViews.php | 7 +++-- .../System/Config/TestConnection.php | 3 +- 5 files changed, 30 insertions(+), 30 deletions(-) rename app/code/Magento/Backup/Model/ResourceModel/View/{GetViewsBackup.php => CreateViewsBackup.php} (75%) diff --git a/app/code/Magento/Backup/Model/Db.php b/app/code/Magento/Backup/Model/Db.php index 971223cdecc4a..084b35448a823 100644 --- a/app/code/Magento/Backup/Model/Db.php +++ b/app/code/Magento/Backup/Model/Db.php @@ -7,7 +7,7 @@ use Magento\Backup\Helper\Data as Helper; use Magento\Backup\Model\ResourceModel\Table\GetListTables; -use Magento\Backup\Model\ResourceModel\View\GetViewsBackup; +use Magento\Backup\Model\ResourceModel\View\CreateViewsBackup; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\RuntimeException; @@ -51,7 +51,7 @@ class Db implements \Magento\Framework\Backup\Db\BackupDbInterface private $getListTables; /** - * @var GetViewsBackup + * @var CreateViewsBackup */ private $getViewsBackup; @@ -61,20 +61,20 @@ class Db implements \Magento\Framework\Backup\Db\BackupDbInterface * @param \Magento\Framework\App\ResourceConnection $resource * @param Helper|null $helper * @param GetListTables|null $getListTables - * @param GetViewsBackup|null $getViewsBackup + * @param CreateViewsBackup|null $getViewsBackup */ public function __construct( - \Magento\Backup\Model\ResourceModel\Db $resourceDb, + ResourceModel\Db $resourceDb, \Magento\Framework\App\ResourceConnection $resource, ?Helper $helper = null, - GetListTables $getListTables = null, - GetViewsBackup $getViewsBackup = null + ?GetListTables $getListTables = null, + ?CreateViewsBackup $getViewsBackup = null ) { $this->_resourceDb = $resourceDb; $this->_resource = $resource; $this->helper = $helper ?? ObjectManager::getInstance()->get(Helper::class); - $this->getListTables = $getListTables ?: ObjectManager::getInstance()->get(GetListTables::class); - $this->getViewsBackup = $getViewsBackup ?: ObjectManager::getInstance()->get(GetViewsBackup::class); + $this->getListTables = $getListTables ?? ObjectManager::getInstance()->get(GetListTables::class); + $this->getViewsBackup = $getViewsBackup ?? ObjectManager::getInstance()->get(CreateViewsBackup::class); } /** diff --git a/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php index 013fc729069f0..73c4221feba3f 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php +++ b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php @@ -10,7 +10,7 @@ use Magento\Framework\App\ResourceConnection; /** - * Class GetListTables + * Provides full list of tables in the database. This list excludes views, to allow different backup process. */ class GetListTables { @@ -30,11 +30,11 @@ public function __construct(ResourceConnection $resource) } /** - * Get base tables + * Get list of database tables excluding views. * * @return array */ - public function execute() + public function execute(): array { return $this->resource->getConnection('backup')->fetchCol( "SHOW FULL TABLES WHERE `Table_type` = ?", diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php b/app/code/Magento/Backup/Model/ResourceModel/View/CreateViewsBackup.php similarity index 75% rename from app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php rename to app/code/Magento/Backup/Model/ResourceModel/View/CreateViewsBackup.php index 5a5f6a4d066e9..51b49dcb9e48a 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php +++ b/app/code/Magento/Backup/Model/ResourceModel/View/CreateViewsBackup.php @@ -12,9 +12,9 @@ use Magento\Framework\DB\Adapter\AdapterInterface; /** - * Class GetViewsBackup + * Creates backup of Views in the database. */ -class GetViewsBackup +class CreateViewsBackup { /** * @var GetListViews @@ -44,27 +44,27 @@ public function __construct( } /** - * Backup + * Write backup data to backup file. * * @param BackupInterface $backup */ - public function execute(BackupInterface $backup) + public function execute(BackupInterface $backup): void { $views = $this->getListViews->execute(); foreach ($views as $view) { $backup->write($this->getViewHeader($view)); $backup->write($this->getDropViewSql($view)); - $backup->write($this->getShowCreateView($view)); + $backup->write($this->getCreateView($view)); } } /** - * Get connection + * Retrieve Database connection for Backup. * * @return AdapterInterface */ - private function getConnection() + private function getConnection(): AdapterInterface { if (!$this->connection) { $this->connection = $this->resourceConnection->getConnection('backup'); @@ -74,12 +74,12 @@ private function getConnection() } /** - * Get show create view + * Get CREATE VIEW query for the specific view. * * @param string $viewName * @return string */ - private function getShowCreateView($viewName) + private function getCreateView(string $viewName): string { $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); $query = 'SHOW CREATE VIEW ' . $quotedViewName; @@ -91,26 +91,26 @@ private function getShowCreateView($viewName) } /** - * Get view header + * Prepare a header for View being dumped. * * @param string $viewName * @return string */ - public function getViewHeader($viewName) + public function getViewHeader(string $viewName): string { $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); return "\n--\n" . "-- Structure for view {$quotedViewName}\n" . "--\n\n"; } /** - * Get drop view SQL + * Make sure that View being created is deleted if already exists. * * @param string $viewName * @return string */ - public function getDropViewSql($viewName) + public function getDropViewSql(string $viewName): string { $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); - return sprintf('DROP VIEW IF EXISTS %s;' . "\n", $quotedViewName); + return sprintf('DROP VIEW IF EXISTS %s;\n', $quotedViewName); } } diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php index 1c408dee8cac3..c76ea2842180b 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php +++ b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php @@ -10,11 +10,12 @@ use Magento\Framework\App\ResourceConnection; /** - * Class GetListViews + * Get list of database views. */ class GetListViews { private const TABLE_TYPE = 'VIEW'; + /** * @var ResourceConnection */ @@ -29,11 +30,11 @@ public function __construct(ResourceConnection $resource) } /** - * Get view tables + * Get list of database views. * * @return array */ - public function execute() + public function execute(): array { return $this->resource->getConnection('backup')->fetchCol( "SHOW FULL TABLES WHERE `Table_type` = ?", diff --git a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php index 8f99ef14dc646..1b17db1a00f6e 100644 --- a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php +++ b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php @@ -6,8 +6,7 @@ namespace Magento\Elasticsearch6\Block\Adminhtml\System\Config; /** - * Elasticsearch 6x test connection block - * @codeCoverageIgnore + * Elasticsearch 6.x test connection block */ class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection { From cce1ecec840ac0d5ca5903e5e8c52c4c6efcb90e Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 28 Feb 2019 21:45:37 -0600 Subject: [PATCH 232/254] magento/magento2#21458: Elasticsearch 6.x support - Updated package dependencis versions --- app/code/Magento/Elasticsearch6/composer.json | 4 ++-- .../Magento/Test/Php/_files/phpcpd/blacklist/common.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json index 6288aff8490bb..c289d8cd3e4e4 100644 --- a/app/code/Magento/Elasticsearch6/composer.json +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -8,8 +8,8 @@ "magento/module-catalog-search": "*", "magento/module-search": "*", "magento/module-store": "*", - "magento/module-elasticsearch": ">=100.3.0", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" + "magento/module-elasticsearch": "*", + "elasticsearch/elasticsearch": "~6.1" }, "suggest": { "magento/module-config": "*" 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 d85b8ba1fa225..13fee19dd5924 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 @@ -208,4 +208,4 @@ Magento/InventoryGroupedProductIndexer/Indexer Magento/Customer/Model/FileUploaderDataResolver.php Magento/Customer/Model/Customer/DataProvider.php Magento/InventoryShippingAdminUi/Ui/DataProvider -Magento/Elasticsearch/Model/Client/Elasticsearch.php +Magento/Elasticsearch6/Model/Client/Elasticsearch.php From e83dc554b129b1641afa168940f2fcf2384f0c9d Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 28 Feb 2019 22:48:00 -0600 Subject: [PATCH 233/254] magento/magento2#21458: Elasticsearch 6.x support --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 7fe110b8dfaef..84e89277a95db 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": "4024498874329e8da2042e7ed9e3bd2f", + "content-hash": "3cdccc93cc990b4212377b1d01a8c4ef", "packages": [ { "name": "braintree/braintree_php", From 81f65fc4dd162b81a7ddc108cb74ec24ee8d5b94 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 1 Mar 2019 00:35:47 -0600 Subject: [PATCH 234/254] Updated CPD blacklist to avoid Elasticsearch client --- .../Magento/Test/Php/_files/phpcpd/blacklist/common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 13fee19dd5924..c4c7c0ee0b388 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 @@ -208,4 +208,4 @@ Magento/InventoryGroupedProductIndexer/Indexer Magento/Customer/Model/FileUploaderDataResolver.php Magento/Customer/Model/Customer/DataProvider.php Magento/InventoryShippingAdminUi/Ui/DataProvider -Magento/Elasticsearch6/Model/Client/Elasticsearch.php +Magento\Elasticsearch6\Model\Client\Elasticsearch From 68857ac5d3a7ed17d50699dece3fe19c34417259 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 1 Mar 2019 01:11:20 -0600 Subject: [PATCH 235/254] PHPCPD blaclist updated with Elasticsearch6 --- .../Magento/Test/Php/_files/phpcpd/blacklist/common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c4c7c0ee0b388..780aca1ceb4f6 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 @@ -208,4 +208,4 @@ Magento/InventoryGroupedProductIndexer/Indexer Magento/Customer/Model/FileUploaderDataResolver.php Magento/Customer/Model/Customer/DataProvider.php Magento/InventoryShippingAdminUi/Ui/DataProvider -Magento\Elasticsearch6\Model\Client\Elasticsearch +Magento/Elasticsearch6/Model/Client/Elasticsearch From 5bbb8bb36a34cda003bfafe7286ef3e208c4ac75 Mon Sep 17 00:00:00 2001 From: Ievgenii Gryshkun <i.gryshkun@gmail.com> Date: Fri, 1 Mar 2019 13:01:18 +0200 Subject: [PATCH 236/254] Cannot return null for non-nullable field SelectedShippingMethod.amount --- .../Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php index 89aa943f9d211..a52745a8bb744 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/ExtractDataFromAddress.php @@ -55,6 +55,7 @@ public function execute(QuoteAddress $address): array 'code' => $address->getShippingMethod(), 'label' => $address->getShippingDescription(), 'free_shipping' => $address->getFreeShipping(), + 'amount' => $address->getShippingAmount() ], 'items_weight' => $address->getWeight(), 'customer_notes' => $address->getCustomerNotes() From c52a4aaee27f4509912ca938b16b751154561d77 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 1 Mar 2019 13:18:37 +0000 Subject: [PATCH 237/254] magento-engcom/magento2ce#2639: Blacklisted elasticsearch client for phpcpd --- .../Magento/Test/Php/_files/phpcpd/blacklist/common.txt | 1 + 1 file changed, 1 insertion(+) 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 780aca1ceb4f6..71dd5e4dd8c27 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 @@ -208,4 +208,5 @@ Magento/InventoryGroupedProductIndexer/Indexer Magento/Customer/Model/FileUploaderDataResolver.php Magento/Customer/Model/Customer/DataProvider.php Magento/InventoryShippingAdminUi/Ui/DataProvider +Magento/Elasticsearch/Model/Client/Elasticsearch Magento/Elasticsearch6/Model/Client/Elasticsearch From fa607c2a5c0c0f754b4839ee6227f7171861dbc3 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 1 Mar 2019 14:35:04 +0000 Subject: [PATCH 238/254] magento-engcom/magento2ce#2639: Corrected phpcpd blacklisting --- .../Magento/Test/Php/_files/phpcpd/blacklist/common.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 71dd5e4dd8c27..0450ae1330cd8 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 @@ -208,5 +208,4 @@ Magento/InventoryGroupedProductIndexer/Indexer Magento/Customer/Model/FileUploaderDataResolver.php Magento/Customer/Model/Customer/DataProvider.php Magento/InventoryShippingAdminUi/Ui/DataProvider -Magento/Elasticsearch/Model/Client/Elasticsearch -Magento/Elasticsearch6/Model/Client/Elasticsearch +Magento/Elasticsearch6/Model/Client From b53c075a08efe6e2cc0a3161932c87a66313b61b Mon Sep 17 00:00:00 2001 From: Kevin Kozan <kkozan@magento.com> Date: Fri, 1 Mar 2019 09:06:52 -0600 Subject: [PATCH 239/254] MQE-1460: Deliver weekly PR - Fixed StorefrontCheckTaxAddingValidVATIdTest --- .../ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml | 5 +++++ .../StorefrontCustomerDashboardAccountInformationSection.xml | 5 +++-- .../Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml index 8763b864a26d3..ef956293d367b 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -72,6 +72,11 @@ <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"> + <fillField stepKey="selectState" selector="{{StorefrontCustomerAddressSection.stateProvinceFill}}" userInput="{{Address.state}}"/> + </actionGroup> + <actionGroup name="VerifyCustomerBillingAddress"> <arguments> <argument name="address"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml index e888343a5be2a..93e7bf71b0894 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerDashboardAccountInformationSection.xml @@ -10,8 +10,8 @@ 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="link" selector=".action.edit" timeout="15"/> - <element name="changePassword" type="link" selector=".action.change-password" timeout="15"/> + <element name="edit" type="block" selector=".action.edit" timeout="15"/> + <element name="changePassword" type="block" selector=".action.change-password" timeout="15"/> </section> <section name="StorefrontCustomerAddressSection"> <element name="firstName" type="input" selector="#firstname"/> @@ -22,6 +22,7 @@ <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"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml index 229e81e877292..ab805193854b0 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml @@ -72,7 +72,7 @@ </actionGroup> <!--Go to My account > Address book--> - <actionGroup ref="EnterCustomerAddressInfo" stepKey="enterAddressInfo"> + <actionGroup ref="EnterCustomerAddressInfoFillState" stepKey="enterAddressInfo"> <argument name="Address" value="UK_Simple_Address"/> </actionGroup> From 5b2dc82a2fadceb79975589ad952b33a1a1d8351 Mon Sep 17 00:00:00 2001 From: Tom Reece <treece@adobe.com> Date: Fri, 1 Mar 2019 09:34:10 -0600 Subject: [PATCH 240/254] MQE-1460: Deliver weekly PR - Clear filters before selecting all customers --- .../Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml index 7c4e9fdfd848f..fb083f39ad387 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml @@ -70,6 +70,7 @@ </annotations> <remove keyForRemoval="filterCustomer"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters" before="selectCustomer"/> <actionGroup ref="AdminSelectAllCustomers" stepKey="selectCustomer"/> </test> </tests> From 3e0c7e7c2227549bfcc663bb1d37d3a49d7fed31 Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Tue, 26 Feb 2019 11:58:10 +0100 Subject: [PATCH 241/254] Elasticsearch6 implementation. --- .../AdvancedSearch/etc/adminhtml/system.xml | 6 +- .../System/Config/TestConnection.php | 32 + app/code/Magento/Elasticsearch6/LICENSE.txt | 48 ++ .../Magento/Elasticsearch6/LICENSE_AFL.txt | 48 ++ .../FieldName/Resolver/DefaultResolver.php | 34 ++ .../Model/Client/Elasticsearch.php | 332 +++++++++++ .../Magento/Elasticsearch6/Model/Config.php | 57 ++ .../Model/DataProvider/Suggestions.php | 271 +++++++++ app/code/Magento/Elasticsearch6/README.md | 2 + .../Resolver/DefaultResolverTest.php | 118 ++++ .../Unit/Model/Client/ElasticsearchTest.php | 562 ++++++++++++++++++ .../Model/DataProvider/SuggestionsTest.php | 183 ++++++ app/code/Magento/Elasticsearch6/composer.json | 26 + .../Elasticsearch6/etc/adminhtml/system.xml | 85 +++ .../Magento/Elasticsearch6/etc/config.xml | 20 + app/code/Magento/Elasticsearch6/etc/di.xml | 165 +++++ .../Magento/Elasticsearch6/etc/module.xml | 17 + .../Magento/Elasticsearch6/registration.php | 11 + 18 files changed, 2014 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php create mode 100644 app/code/Magento/Elasticsearch6/LICENSE.txt create mode 100644 app/code/Magento/Elasticsearch6/LICENSE_AFL.txt create mode 100644 app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php create mode 100644 app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php create mode 100644 app/code/Magento/Elasticsearch6/Model/Config.php create mode 100644 app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php create mode 100644 app/code/Magento/Elasticsearch6/README.md create mode 100644 app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolverTest.php create mode 100644 app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php create mode 100644 app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php create mode 100644 app/code/Magento/Elasticsearch6/composer.json create mode 100644 app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml create mode 100644 app/code/Magento/Elasticsearch6/etc/config.xml create mode 100644 app/code/Magento/Elasticsearch6/etc/di.xml create mode 100644 app/code/Magento/Elasticsearch6/etc/module.xml create mode 100644 app/code/Magento/Elasticsearch6/registration.php diff --git a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml index fa7774f5cec1d..2c4f7fca1834b 100644 --- a/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml +++ b/app/code/Magento/AdvancedSearch/etc/adminhtml/system.xml @@ -48,18 +48,18 @@ </depends> </field> <!--<group id="suggestions">--> - <field id="search_suggestion_enabled" translate="label comment" type="select" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="search_suggestion_enabled" translate="label comment" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Enable Search Suggestions</label> <comment>When you enable this option your site may slow down.</comment> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="search_suggestion_count" translate="label" type="text" sortOrder="71" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="search_suggestion_count" translate="label" type="text" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Search Suggestions Count</label> <depends> <field id="search_suggestion_enabled">1</field> </depends> </field> - <field id="search_suggestion_count_results_enabled" translate="label" type="select" sortOrder="72" showInDefault="1" showInWebsite="1" showInStore="1"> + <field id="search_suggestion_count_results_enabled" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Show Results Count for Each Suggestion</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <comment>When you enable this option your site may slow down.</comment> diff --git a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php new file mode 100644 index 0000000000000..5573d959fa372 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\Block\Adminhtml\System\Config; + +/** + * Elasticsearch 6x 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_elasticsearch6_server_hostname', + 'port' => 'catalog_search_elasticsearch6_server_port', + 'index' => 'catalog_search_elasticsearch6_index_prefix', + 'enableAuth' => 'catalog_search_elasticsearch6_enable_auth', + 'username' => 'catalog_search_elasticsearch6_username', + 'password' => 'catalog_search_elasticsearch6_password', + 'timeout' => 'catalog_search_elasticsearch6_server_timeout', + ]; + + return array_merge(parent::_getFieldMapping(), $fields); + } +} diff --git a/app/code/Magento/Elasticsearch6/LICENSE.txt b/app/code/Magento/Elasticsearch6/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/Elasticsearch6/LICENSE.txt @@ -0,0 +1,48 @@ + +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/Elasticsearch6/LICENSE_AFL.txt b/app/code/Magento/Elasticsearch6/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +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/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php new file mode 100644 index 0000000000000..2420335a95dc5 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; + +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; + +/** + * Default name resolver. + */ +class DefaultResolver extends \Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver +{ + /** + * Get field name. + * + * @param AttributeAdapter $attribute + * @param array $context + * @return string + */ + public function getFieldName(AttributeAdapter $attribute, $context = []): ?string + { + $fieldName = parent::getFieldName($attribute, $context); + + if ($fieldName === '_all') { + $fieldName = '_search'; + } + + return $fieldName; + } +} diff --git a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php new file mode 100644 index 0000000000000..af39b24acda56 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php @@ -0,0 +1,332 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\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 = []) + { + $host = preg_replace('/http[s]?:\/\//i', '', $options['hostname']); + $protocol = parse_url($options['hostname'], PHP_URL_SCHEME); + if (!$protocol) { + $protocol = 'http'; + } + if (!empty($options['port'])) { + $host .= ':' . $options['port']; + } + if (!empty($options['enableAuth']) && ($options['enableAuth'] == 1)) { + $host = sprintf('%s://%s:%s@%s', $protocol, $options['username'], $options['password'], $host); + } + + $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]); + } + + /** + * Exists alias. + * + * @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 => [ + 'properties' => [ + '_search' => [ + 'type' => 'text' + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => false, + 'copy_to' => '_search' + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'int', + ], + ], + ], + ], + ], + ], + ]; + + 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/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php new file mode 100644 index 0000000000000..aec00d92ce029 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/Config.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Elasticsearch6\Model; + +use Magento\Framework\Search\EngineResolverInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\AdvancedSearch\Model\Client\ClientResolver; + +/** + * Elasticsearch6 config model + */ +class Config extends \Magento\Elasticsearch\Model\Config +{ + /** + * Search engine name + */ + private const ENGINE_NAME_6 = 'elasticsearch6'; + + /** + * @var EngineResolverInterface + */ + private $engineResolver; + + /** + * Constructor + * + * @param ScopeConfigInterface $scopeConfig + * @param ClientResolver|null $clientResolver + * @param EngineResolverInterface|null $engineResolver + * @param string|null $prefix + */ + public function __construct( + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\AdvancedSearch\Model\Client\ClientResolver $clientResolver = null, + \Magento\Framework\Search\EngineResolverInterface $engineResolver = null, + $prefix = null + ) { + parent::__construct($scopeConfig, $clientResolver, $engineResolver, $prefix); + $this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class); + } + + /** + * Return true if third party search engine is used + * + * @return bool + * @since 100.1.0 + */ + public function isElasticsearchEnabled() + { + return in_array($this->engineResolver->getCurrentSearchEngine(), [self::ENGINE_NAME_6]); + } +} diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php new file mode 100644 index 0000000000000..24412fa6baec8 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php @@ -0,0 +1,271 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\Model\DataProvider; + +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProviderInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Search\Model\QueryInterface; +use Magento\AdvancedSearch\Model\SuggestedQueriesInterface; +use Magento\Elasticsearch6\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\Store\Model\StoreManagerInterface as StoreManager; + +/** + * Class Suggestions + */ +class Suggestions implements SuggestedQueriesInterface +{ + /** + * @var Config + */ + private $config; + + /** + * @var QueryResultFactory + */ + private $queryResultFactory; + + /** + * @var ConnectionManager + */ + private $connectionManager; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var SearchIndexNameResolver + */ + private $searchIndexNameResolver; + + /** + * @var StoreManager + */ + private $storeManager; + + /** + * @var FieldProviderInterface + */ + private $fieldProvider; + + /** + * Suggestions constructor. + * + * @param ScopeConfigInterface $scopeConfig + * @param Config $config + * @param QueryResultFactory $queryResultFactory + * @param ConnectionManager $connectionManager + * @param SearchIndexNameResolver $searchIndexNameResolver + * @param StoreManager $storeManager + * @param FieldProviderInterface $fieldProvider + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + Config $config, + QueryResultFactory $queryResultFactory, + ConnectionManager $connectionManager, + SearchIndexNameResolver $searchIndexNameResolver, + StoreManager $storeManager, + FieldProviderInterface $fieldProvider + ) { + $this->queryResultFactory = $queryResultFactory; + $this->connectionManager = $connectionManager; + $this->scopeConfig = $scopeConfig; + $this->config = $config; + $this->searchIndexNameResolver = $searchIndexNameResolver; + $this->storeManager = $storeManager; + $this->fieldProvider = $fieldProvider; + } + + /** + * @inheritdoc + */ + public function getItems(QueryInterface $query) + { + $result = []; + if ($this->isSuggestionsAllowed()) { + $isResultsCountEnabled = $this->isResultsCountEnabled(); + + foreach ($this->getSuggestions($query) as $suggestion) { + $count = null; + if ($isResultsCountEnabled) { + $count = isset($suggestion['freq']) ? $suggestion['freq'] : null; + } + $result[] = $this->queryResultFactory->create( + [ + 'queryText' => $suggestion['text'], + 'resultsCount' => $count, + ] + ); + } + } + + return $result; + } + + /** + * @inheritdoc + */ + public function isResultsCountEnabled() + { + return $this->scopeConfig->isSetFlag( + SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT_RESULTS_ENABLED, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * Get Suggestions + * + * @param QueryInterface $query + * + * @return array + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getSuggestions(QueryInterface $query) + { + $suggestions = []; + $searchSuggestionsCount = $this->getSearchSuggestionsCount(); + + $searchQuery = $this->initQuery($query); + $searchQuery = $this->addSuggestFields($searchQuery, $searchSuggestionsCount); + + $result = $this->fetchQuery($searchQuery); + + if (is_array($result)) { + foreach ($result['suggest'] ?? [] as $suggest) { + foreach ($suggest as $token) { + foreach ($token['options'] ?? [] as $key => $suggestion) { + $suggestions[$suggestion['score'] . '_' . $key] = $suggestion; + } + } + } + ksort($suggestions); + $texts = array_unique(array_column($suggestions, 'text')); + $suggestions = array_slice(array_intersect_key(array_values($suggestions), $texts), 0, $searchSuggestionsCount); + } + + return $suggestions; + } + + /** + * Init Search Query + * + * @param string $query + * + * @return array + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function initQuery($query) + { + $searchQuery = [ + 'index' => $this->searchIndexNameResolver->getIndexName( + $this->storeManager->getStore()->getId(), + Config::ELASTICSEARCH_TYPE_DEFAULT + ), + 'type' => Config::ELASTICSEARCH_TYPE_DEFAULT, + 'body' => [ + 'suggest' => [ + 'text' => $query->getQueryText() + ] + ], + ]; + + return $searchQuery; + } + + /** + * Build Suggest on searchable fields. + * + * @param array $searchQuery + * @param int $searchSuggestionsCount + * + * @return array + */ + private function addSuggestFields($searchQuery, $searchSuggestionsCount) + { + $fields = $this->getSuggestFields(); + foreach ($fields as $field) { + $searchQuery['body']['suggest']['phrase_' . $field] = [ + 'phrase' => [ + 'field' => $field, + 'analyzer' => 'standard', + 'size' => $searchSuggestionsCount, + 'max_errors' => 1, + 'direct_generator' => [ + [ + 'field' => $field, + 'min_word_length' => 3, + 'min_doc_freq' => 1, + ] + ], + ], + ]; + } + + return $searchQuery; + } + + /** + * Get fields to build suggest query on. + * + * @return array + */ + private function getSuggestFields() + { + $fields = array_filter($this->fieldProvider->getFields(), function ($field) { + return (($field['type'] ?? null) === 'text') && (($field['index'] ?? null) !== false); + }); + + return array_keys($fields); + } + + /** + * Fetch Query + * + * @param array $query + * @return array + */ + private function fetchQuery(array $query) + { + return $this->connectionManager->getConnection()->query($query); + } + + /** + * Get search suggestions Max Count from config + * + * @return int + */ + private function getSearchSuggestionsCount() + { + return (int) $this->scopeConfig->getValue( + SuggestedQueriesInterface::SEARCH_SUGGESTION_COUNT, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * Is Search Suggestions Allowed + * + * @return bool + */ + private function isSuggestionsAllowed() + { + $isSuggestionsEnabled = $this->scopeConfig->isSetFlag( + SuggestedQueriesInterface::SEARCH_SUGGESTION_ENABLED, + ScopeInterface::SCOPE_STORE + ); + $isEnabled = $this->config->isElasticsearchEnabled(); + $isSuggestionsAllowed = ($isEnabled && $isSuggestionsEnabled); + + return $isSuggestionsAllowed; + } +} diff --git a/app/code/Magento/Elasticsearch6/README.md b/app/code/Magento/Elasticsearch6/README.md new file mode 100644 index 0000000000000..8bf95ad95d147 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/README.md @@ -0,0 +1,2 @@ +Magento\Elasticsearch module allows to use Elastic search engine (v6) for product searching capabilities. +The module implements Magento\Search library interfaces. diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolverTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolverTest.php new file mode 100644 index 0000000000000..c2a70d3f082f0 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolverTest.php @@ -0,0 +1,118 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Elasticsearch6\Test\Unit\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; + +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ResolverInterface + as FieldTypeResolver; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldType\ConverterInterface + as FieldTypeConverterInterface; +use Magento\Elasticsearch6\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver; + +/** + * @SuppressWarnings(PHPMD) + */ +class DefaultResolverTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var DefaultResolver + */ + private $resolver; + + /** + * @var FieldTypeResolver + */ + private $fieldTypeResolver; + + /** + * @var FieldTypeConverterInterface + */ + private $fieldTypeConverter; + + /** + * Set up test environment + * + * @return void + */ + protected function setUp() + { + $objectManager = new ObjectManagerHelper($this); + $this->fieldTypeResolver = $this->getMockBuilder(FieldTypeResolver::class) + ->disableOriginalConstructor() + ->setMethods(['getFieldType']) + ->getMockForAbstractClass(); + $this->fieldTypeConverter = $this->getMockBuilder(FieldTypeConverterInterface::class) + ->disableOriginalConstructor() + ->setMethods(['convert']) + ->getMockForAbstractClass(); + + $this->resolver = $objectManager->getObject( + DefaultResolver::class, + [ + 'fieldTypeResolver' => $this->fieldTypeResolver, + 'fieldTypeConverter' => $this->fieldTypeConverter + ] + ); + } + + /** + * @dataProvider getFieldNameProvider + * @param $fieldType + * @param $attributeCode + * @param $frontendInput + * @param $context + * @param $expected + * @return void + */ + public function testGetFieldName( + $fieldType, + $attributeCode, + $frontendInput, + $context, + $expected + ) { + $this->fieldTypeConverter->expects($this->any()) + ->method('convert') + ->willReturn('string'); + $attributeMock = $this->getMockBuilder(AttributeAdapter::class) + ->disableOriginalConstructor() + ->setMethods(['getAttributeCode', 'getFrontendInput']) + ->getMock(); + $attributeMock->expects($this->any()) + ->method('getAttributeCode') + ->willReturn($attributeCode); + $attributeMock->expects($this->any()) + ->method('getFrontendInput') + ->willReturn($frontendInput); + $this->fieldTypeResolver->expects($this->any()) + ->method('getFieldType') + ->willReturn($fieldType); + + $this->assertEquals( + $expected, + $this->resolver->getFieldName($attributeMock, $context) + ); + } + + /** + * @return array + */ + public function getFieldNameProvider() + { + return [ + ['', 'code', '', [], 'code'], + ['', 'code', '', ['type' => 'default'], 'code'], + ['string', '*', '', ['type' => 'default'], '_search'], + ['', 'code', '', ['type' => 'default'], 'code'], + ['', 'code', 'select', ['type' => 'default'], 'code'], + ['', 'code', 'boolean', ['type' => 'default'], 'code'], + ['', 'code', '', ['type' => 'type'], 'sort_code'], + ]; + } +} diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php new file mode 100644 index 0000000000000..8276d0dd8dbe8 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php @@ -0,0 +1,562 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\Test\Unit\Model\Client; + +use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +class ElasticsearchTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var ElasticsearchClient + */ + protected $model; + + /** + * @var \Elasticsearch\Client|\PHPUnit_Framework_MockObject_MockObject + */ + protected $elasticsearchClientMock; + + /** + * @var \Elasticsearch\Namespaces\IndicesNamespace|\PHPUnit_Framework_MockObject_MockObject + */ + protected $indicesMock; + + /** + * @var ObjectManagerHelper + */ + protected $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' => '6.0.0']]); + + $this->objectManager = new ObjectManagerHelper($this); + $this->model = $this->objectManager->getObject( + \Magento\Elasticsearch6\Model\Client\Elasticsearch::class, + [ + 'options' => $this->getOptions(), + 'elasticsearchClient' => $this->elasticsearchClientMock + ] + ); + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testConstructorOptionsException() + { + $result = $this->objectManager->getObject( + \Magento\Elasticsearch6\Model\Client\Elasticsearch::class, + [ + 'options' => [] + ] + ); + $this->assertNotNull($result); + } + + /** + * Test client creation from the list of options + */ + public function testConstructorWithOptions() + { + $result = $this->objectManager->getObject( + \Magento\Elasticsearch6\Model\Client\Elasticsearch::class, + [ + 'options' => $this->getOptions() + ] + ); + $this->assertNotNull($result); + } + + /** + * 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\Elasticsearch6\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', + 'body' => [ + 'product' => [ + 'properties' => [ + '_search' => [ + 'type' => 'text', + ], + 'name' => [ + 'type' => 'text', + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => false, + 'copy_to' => '_search' + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'int', + ], + ], + ], + ], + ], + ], + ]); + $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', + 'body' => [ + 'product' => [ + 'properties' => [ + '_search' => [ + 'type' => 'text', + ], + 'name' => [ + 'type' => 'text', + ], + ], + 'dynamic_templates' => [ + [ + 'price_mapping' => [ + 'match' => 'price_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'float', + 'store' => true, + ], + ], + ], + [ + 'string_mapping' => [ + 'match' => '*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'text', + 'index' => false, + 'copy_to' => '_search' + ], + ], + ], + [ + 'position_mapping' => [ + 'match' => 'position_*', + 'match_mapping_type' => 'string', + 'mapping' => [ + 'type' => 'int', + ], + ], + ], + ], + ], + ], + ]) + ->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 + */ + protected function getEmptyIndexOption() + { + return [ + 'hostname' => 'localhost', + 'port' => '9200', + 'index' => '', + 'timeout' => 15, + 'enableAuth' => 1, + 'username' => 'user', + 'password' => 'passwd', + ]; + } +} diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php new file mode 100644 index 0000000000000..957edc559fdcb --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/DataProvider/SuggestionsTest.php @@ -0,0 +1,183 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Elasticsearch6\Test\Unit\Model\DataProvider; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Elasticsearch\Model\DataProvider\Suggestions; +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\Store\Model\StoreManagerInterface as StoreManager; +use Magento\Search\Model\QueryInterface; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SuggestionsTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var Suggestions + */ + private $model; + + /** + * @var Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $config; + + /** + * @var QueryResultFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $queryResultFactory; + + /** + * @var ConnectionManager|\PHPUnit_Framework_MockObject_MockObject + */ + private $connectionManager; + + /** + * @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfig; + + /** + * @var SearchIndexNameResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private $searchIndexNameResolver; + + /** + * @var StoreManager|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeManager; + + /** + * @var QueryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $query; + + /** + * Set up test environment + * + * @return void + */ + protected function setUp() + { + $this->config = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Config::class) + ->disableOriginalConstructor() + ->setMethods(['isElasticsearchEnabled']) + ->getMock(); + + $this->queryResultFactory = $this->getMockBuilder(\Magento\Search\Model\QueryResultFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->connectionManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class) + ->disableOriginalConstructor() + ->setMethods(['getConnection']) + ->getMock(); + + $this->scopeConfig = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->searchIndexNameResolver = $this + ->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\SearchIndexNameResolver::class) + ->disableOriginalConstructor() + ->setMethods(['getIndexName']) + ->getMock(); + + $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->query = $this->getMockBuilder(\Magento\Search\Model\QueryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $objectManager = new ObjectManagerHelper($this); + + $this->model = $objectManager->getObject( + \Magento\Elasticsearch6\Model\DataProvider\Suggestions::class, + [ + 'queryResultFactory' => $this->queryResultFactory, + 'connectionManager' => $this->connectionManager, + 'scopeConfig' => $this->scopeConfig, + 'config' => $this->config, + 'searchIndexNameResolver' => $this->searchIndexNameResolver, + 'storeManager' => $this->storeManager + ] + ); + } + + /** + * Test getItems() method + */ + public function testGetItems() + { + $this->scopeConfig->expects($this->any()) + ->method('getValue') + ->willReturn(1); + + $this->config->expects($this->any()) + ->method('isElasticsearchEnabled') + ->willReturn(1); + + $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->storeManager->expects($this->any()) + ->method('getStore') + ->willReturn($store); + + $store->expects($this->any()) + ->method('getId') + ->willReturn(1); + + $this->searchIndexNameResolver->expects($this->any()) + ->method('getIndexName') + ->willReturn('magento2_product_1'); + + $this->query->expects($this->any()) + ->method('getQueryText') + ->willReturn('query'); + + $client = $this->getMockBuilder(\Magento\Elasticsearch6\Model\Client\Elasticsearch::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->connectionManager->expects($this->any()) + ->method('getConnection') + ->willReturn($client); + + $client->expects($this->any()) + ->method('query') + ->willReturn([ + 'suggest' => [ + 'phrase_field' => [ + 'options' => [ + 'text' => 'query', + 'score' => 1, + 'freq' => 1, + ] + ], + ], + ]); + + $query = $this->getMockBuilder(\Magento\Search\Model\QueryResult::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->queryResultFactory->expects($this->any()) + ->method('create') + ->willReturn($query); + + $this->assertInternalType('array', $this->model->getItems($this->query)); + } +} diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json new file mode 100644 index 0000000000000..ec9fc3b463f5d --- /dev/null +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-elasticsearch-6", + "description": "N/A", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-elasticsearch": ">=100.3.0", + "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" + }, + "suggest": { + "magento/module-config": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\Elasticsearch6\\": "" + } + } +} diff --git a/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..067a0acb8c908 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/etc/adminhtml/system.xml @@ -0,0 +1,85 @@ +<?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 6.0+ --> + <field id="elasticsearch6_server_hostname" translate="label" type="text" sortOrder="71" + showInDefault="1" showInWebsite="0" showInStore="0"> + <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"> + <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"> + <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"> + <label>Enable Elasticsearch HTTP Auth</label> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <depends> + <field id="engine">elasticsearch6</field> + </depends> + </field> + + <field id="elasticsearch6_username" translate="label" type="text" sortOrder="75" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch HTTP Username</label> + <depends> + <field id="engine">elasticsearch6</field> + <field id="elasticsearch6_enable_auth">1</field> + </depends> + </field> + + <field id="elasticsearch6_password" translate="label" type="text" sortOrder="76" showInDefault="1" + showInWebsite="0" showInStore="0"> + <label>Elasticsearch HTTP Password</label> + <depends> + <field id="engine">elasticsearch6</field> + <field id="elasticsearch6_enable_auth">1</field> + </depends> + </field> + + <field id="elasticsearch6_server_timeout" translate="label" type="text" sortOrder="77" showInDefault="1" + showInWebsite="0" showInStore="0"> + <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"> + <label/> + <button_label>Test Connection</button_label> + <frontend_model>Magento\Elasticsearch6\Block\Adminhtml\System\Config\TestConnection</frontend_model> + <depends> + <field id="engine">elasticsearch6</field> + </depends> + </field> + </group> + </section> + </system> +</config> diff --git a/app/code/Magento/Elasticsearch6/etc/config.xml b/app/code/Magento/Elasticsearch6/etc/config.xml new file mode 100644 index 0000000000000..047ae977fdef1 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/etc/config.xml @@ -0,0 +1,20 @@ +<?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> + <elasticsearch6_server_hostname>localhost</elasticsearch6_server_hostname> + <elasticsearch6_server_port>9200</elasticsearch6_server_port> + <elasticsearch6_index_prefix>magento2</elasticsearch6_index_prefix> + <elasticsearch6_enable_auth>0</elasticsearch6_enable_auth> + <elasticsearch6_server_timeout>15</elasticsearch6_server_timeout> + </search> + </catalog> + </default> +</config> diff --git a/app/code/Magento/Elasticsearch6/etc/di.xml b/app/code/Magento/Elasticsearch6/etc/di.xml new file mode 100644 index 0000000000000..25eff42fd3442 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/etc/di.xml @@ -0,0 +1,165 @@ +<?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\Search\Model\Adminhtml\System\Config\Source\Engine"> + <arguments> + <argument name="engines" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">Elasticsearch 6.0+</item> + </argument> + </arguments> + </type> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Adapter\BatchDataMapper\CategoryFieldsProviderProxy"> + <arguments> + <argument name="categoryFieldsProviders" xsi:type="array"> + <item name="elasticsearch6" 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="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"> + <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Client\ElasticsearchFactory</item> + </argument> + <argument name="clientOptions" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">\Magento\Elasticsearch6\Model\Config</item> + </argument> + </arguments> + </type> + + <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory"> + <arguments> + <argument name="handlers" xsi:type="array"> + <item name="elasticsearch6" 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="elasticsearch6" 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="elasticsearch6" 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="elasticsearch6" xsi:type="string">Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Adapter</item> + </argument> + </arguments> + </type> + + <type name="Magento\Search\Model\EngineResolver"> + <arguments> + <argument name="engines" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">elasticsearch6</item> + </argument> + </arguments> + </type> + + <virtualType name="Magento\Elasticsearch6\Model\Client\ElasticsearchFactory" type="Magento\AdvancedSearch\Model\Client\ClientFactory"> + <arguments> + <argument name="clientClass" xsi:type="string">Magento\Elasticsearch6\Model\Client\Elasticsearch</argument> + </arguments> + </virtualType> + + <type name="Magento\Elasticsearch\Elasticsearch5\Model\Client\ClientFactoryProxy"> + <arguments> + <argument name="clientFactories" xsi:type="array"> + <item name="elasticsearch6" xsi:type="object">Magento\Elasticsearch6\Model\Client\ElasticsearchFactory</item> + </argument> + </arguments> + </type> + + <type name="Magento\Framework\Search\Dynamic\IntervalFactory"> + <arguments> + <argument name="intervals" xsi:type="array"> + <item name="elasticsearch6" 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="elasticsearch6" xsi:type="string">Magento\Elasticsearch\SearchAdapter\Dynamic\DataProvider</item> + </argument> + </arguments> + </type> + + + <type name="Magento\AdvancedSearch\Model\SuggestedQueries"> + <arguments> + <argument name="data" xsi:type="array"> + <item name="elasticsearch6" xsi:type="string">Magento\Elasticsearch6\Model\DataProvider\Suggestions</item> + </argument> + </arguments> + </type> + + <type name="Magento\Elasticsearch6\Model\DataProvider\Suggestions"> + <arguments> + <argument name="fieldProvider" xsi:type="object">elasticsearch5FieldProvider</argument> + </arguments> + </type> + + <virtualType name="elasticsearch6FieldNameResolver" 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\Elasticsearch6\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver</item> + </argument> + </arguments> + </virtualType> + + <virtualType name="Magento\Elasticsearch6\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">elasticsearch6FieldNameResolver</argument> + </arguments> + </virtualType> + + <type name="Magento\Search\Model\Search\PageSizeProvider"> + <arguments> + <argument name="pageSizeBySearchEngine" xsi:type="array"> + <item name="elasticsearch6" xsi:type="number">2147483647</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/Elasticsearch6/etc/module.xml b/app/code/Magento/Elasticsearch6/etc/module.xml new file mode 100644 index 0000000000000..8756793621aa8 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/etc/module.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:Module/etc/module.xsd"> + <module name="Magento_Elasticsearch6"> + <sequence> + <module name="Magento_CatalogSearch"/> + <module name="Magento_Search"/> + <module name="Magento_AdvancedSearch"/> + <module name="Magento_Elasticsearch"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/Elasticsearch6/registration.php b/app/code/Magento/Elasticsearch6/registration.php new file mode 100644 index 0000000000000..7ab10e996eb8c --- /dev/null +++ b/app/code/Magento/Elasticsearch6/registration.php @@ -0,0 +1,11 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_Elasticsearch6', + __DIR__ +); From 306255b2009757546c859fc3eab3de6a8245ff28 Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Tue, 26 Feb 2019 12:28:56 +0100 Subject: [PATCH 242/254] Allow more recent version of ES client. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7120fe77b0e55..28ffb92218752 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "colinmollenhour/credis": "1.10.0", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", - "elasticsearch/elasticsearch": "~2.0|~5.1", + "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1", "magento/composer": "~1.4.0", "magento/magento-composer-installer": ">=0.1.11", "magento/zendframework1": "~1.14.1", From 45458e65c51f4457d8764abd898ac81452c34fea Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Tue, 26 Feb 2019 18:16:36 +0100 Subject: [PATCH 243/254] Update composer.json replace. --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 28ffb92218752..90954101d8d40 100644 --- a/composer.json +++ b/composer.json @@ -149,6 +149,7 @@ "magento/module-downloadable-import-export": "*", "magento/module-eav": "*", "magento/module-elasticsearch": "*", + "magento/module-elasticsearch-6": "*", "magento/module-email": "*", "magento/module-encryption-key": "*", "magento/module-fedex": "*", From 9734c2fbd903576c5f05f4ae734505db3f9139cd Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Wed, 27 Feb 2019 15:57:54 +0100 Subject: [PATCH 244/254] Update module dependencies. --- app/code/Magento/Elasticsearch6/composer.json | 4 ++++ app/code/Magento/Elasticsearch6/etc/module.xml | 1 + 2 files changed, 5 insertions(+) diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json index ec9fc3b463f5d..6288aff8490bb 100644 --- a/app/code/Magento/Elasticsearch6/composer.json +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -4,6 +4,10 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-advanced-search": "*", + "magento/module-catalog-search": "*", + "magento/module-search": "*", + "magento/module-store": "*", "magento/module-elasticsearch": ">=100.3.0", "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" }, diff --git a/app/code/Magento/Elasticsearch6/etc/module.xml b/app/code/Magento/Elasticsearch6/etc/module.xml index 8756793621aa8..4fde2394dfbdd 100644 --- a/app/code/Magento/Elasticsearch6/etc/module.xml +++ b/app/code/Magento/Elasticsearch6/etc/module.xml @@ -11,6 +11,7 @@ <module name="Magento_CatalogSearch"/> <module name="Magento_Search"/> <module name="Magento_AdvancedSearch"/> + <module name="Magento_Store"/> <module name="Magento_Elasticsearch"/> </sequence> </module> From ae22130b706ad2e1224583e5303c17af6228b145 Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Wed, 27 Feb 2019 16:00:06 +0100 Subject: [PATCH 245/254] Remove excessive since. --- app/code/Magento/Elasticsearch6/Model/Config.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php index aec00d92ce029..f21101f001d33 100644 --- a/app/code/Magento/Elasticsearch6/Model/Config.php +++ b/app/code/Magento/Elasticsearch6/Model/Config.php @@ -48,7 +48,6 @@ public function __construct( * Return true if third party search engine is used * * @return bool - * @since 100.1.0 */ public function isElasticsearchEnabled() { From c73a128a24041f038b13a683133768c6b42764e3 Mon Sep 17 00:00:00 2001 From: Romain Ruaud <romain.ruaud@smile.fr> Date: Wed, 27 Feb 2019 16:02:29 +0100 Subject: [PATCH 246/254] Set dependencies as required. --- app/code/Magento/Elasticsearch6/Model/Config.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Model/Config.php b/app/code/Magento/Elasticsearch6/Model/Config.php index f21101f001d33..1a989e2705fdd 100644 --- a/app/code/Magento/Elasticsearch6/Model/Config.php +++ b/app/code/Magento/Elasticsearch6/Model/Config.php @@ -36,12 +36,12 @@ class Config extends \Magento\Elasticsearch\Model\Config */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\AdvancedSearch\Model\Client\ClientResolver $clientResolver = null, - \Magento\Framework\Search\EngineResolverInterface $engineResolver = null, + \Magento\AdvancedSearch\Model\Client\ClientResolver $clientResolver, + \Magento\Framework\Search\EngineResolverInterface $engineResolver, $prefix = null ) { parent::__construct($scopeConfig, $clientResolver, $engineResolver, $prefix); - $this->engineResolver = $engineResolver ?: ObjectManager::getInstance()->get(EngineResolverInterface::class); + $this->engineResolver = $engineResolver; } /** From a9c865a25074452588b99c388afaa0a8e852d87c Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 28 Feb 2019 17:09:09 +0000 Subject: [PATCH 247/254] magento/magento2#21458: Fixed static tests --- .../Block/Adminhtml/System/Config/TestConnection.php | 2 +- .../FieldProvider/FieldName/Resolver/DefaultResolver.php | 3 ++- .../Elasticsearch6/Model/DataProvider/Suggestions.php | 8 ++++++-- .../Magento/Test/Php/_files/phpcpd/blacklist/common.txt | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php index 5573d959fa372..8f99ef14dc646 100644 --- a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php +++ b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php @@ -12,7 +12,7 @@ class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection { /** - * {@inheritdoc} + * @inheritdoc */ protected function _getFieldMapping() { diff --git a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php index 2420335a95dc5..7532927f1dc85 100644 --- a/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php +++ b/app/code/Magento/Elasticsearch6/Model/Adapter/FieldMapper/Product/FieldProvider/FieldName/Resolver/DefaultResolver.php @@ -8,11 +8,12 @@ namespace Magento\Elasticsearch6\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver; use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeAdapter; +use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver as Base; /** * Default name resolver. */ -class DefaultResolver extends \Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\Resolver\DefaultResolver +class DefaultResolver extends Base { /** * Get field name. diff --git a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php index 24412fa6baec8..77e1270f54fc2 100644 --- a/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php +++ b/app/code/Magento/Elasticsearch6/Model/DataProvider/Suggestions.php @@ -150,7 +150,11 @@ private function getSuggestions(QueryInterface $query) } ksort($suggestions); $texts = array_unique(array_column($suggestions, 'text')); - $suggestions = array_slice(array_intersect_key(array_values($suggestions), $texts), 0, $searchSuggestionsCount); + $suggestions = array_slice( + array_intersect_key(array_values($suggestions), $texts), + 0, + $searchSuggestionsCount + ); } return $suggestions; @@ -186,7 +190,7 @@ private function initQuery($query) * Build Suggest on searchable fields. * * @param array $searchQuery - * @param int $searchSuggestionsCount + * @param int $searchSuggestionsCount * * @return array */ 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 837fef7a1935d..d85b8ba1fa225 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 @@ -208,3 +208,4 @@ Magento/InventoryGroupedProductIndexer/Indexer Magento/Customer/Model/FileUploaderDataResolver.php Magento/Customer/Model/Customer/DataProvider.php Magento/InventoryShippingAdminUi/Ui/DataProvider +Magento/Elasticsearch/Model/Client/Elasticsearch.php From b4e41fe7d16b4e023edc1a117ec719053df42269 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 28 Feb 2019 21:45:37 -0600 Subject: [PATCH 248/254] magento/magento2#21458: Elasticsearch 6.x support - Updated package dependencis versions --- app/code/Magento/Elasticsearch6/composer.json | 4 ++-- .../Magento/Test/Php/_files/phpcpd/blacklist/common.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/composer.json b/app/code/Magento/Elasticsearch6/composer.json index 6288aff8490bb..c289d8cd3e4e4 100644 --- a/app/code/Magento/Elasticsearch6/composer.json +++ b/app/code/Magento/Elasticsearch6/composer.json @@ -8,8 +8,8 @@ "magento/module-catalog-search": "*", "magento/module-search": "*", "magento/module-store": "*", - "magento/module-elasticsearch": ">=100.3.0", - "elasticsearch/elasticsearch": "~2.0|~5.1|~6.1" + "magento/module-elasticsearch": "*", + "elasticsearch/elasticsearch": "~6.1" }, "suggest": { "magento/module-config": "*" 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 d85b8ba1fa225..13fee19dd5924 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 @@ -208,4 +208,4 @@ Magento/InventoryGroupedProductIndexer/Indexer Magento/Customer/Model/FileUploaderDataResolver.php Magento/Customer/Model/Customer/DataProvider.php Magento/InventoryShippingAdminUi/Ui/DataProvider -Magento/Elasticsearch/Model/Client/Elasticsearch.php +Magento/Elasticsearch6/Model/Client/Elasticsearch.php From 89fd18d6cee361e57b344479d9fe4d1cd739ddd7 Mon Sep 17 00:00:00 2001 From: Valerii Naida <vnayda@adobe.com> Date: Fri, 1 Mar 2019 11:02:00 -0600 Subject: [PATCH 249/254] GraphQL-381: Test coverage of Adding configurable product to shopping cart --- .../Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php index 02ce5d8495fa6..b3f16c8734203 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddConfigurableProductToCartTest.php @@ -60,7 +60,6 @@ public function testAddConfigurableProductToCart() self::assertEquals($variantSku, $cartItems[0]['product']['sku']); } - /** * @magentoApiDataFixture Magento/Catalog/_files/multiple_mixed_products.php * @magentoApiDataFixture Magento/Checkout/_files/active_quote.php @@ -116,7 +115,7 @@ private function getMaskedQuoteId() * * @return string */ - private function getAddConfigurableProductMutationQuery(string $maskedQuoteId, string $variantSku, int $qty) : string + private function getAddConfigurableProductMutationQuery(string $maskedQuoteId, string $variantSku, int $qty): string { return <<<QUERY mutation { From d83b16ac796822c5e65c85759730cc8989e92469 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 1 Mar 2019 11:02:25 -0600 Subject: [PATCH 250/254] magento/magento2#21458: Elasticsearch 6.x support - minor code quality improvements --- .../Block/Adminhtml/System/Config/TestConnection.php | 3 +-- .../Magento/Test/Php/_files/phpcpd/blacklist/common.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php index 8f99ef14dc646..1b17db1a00f6e 100644 --- a/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php +++ b/app/code/Magento/Elasticsearch6/Block/Adminhtml/System/Config/TestConnection.php @@ -6,8 +6,7 @@ namespace Magento\Elasticsearch6\Block\Adminhtml\System\Config; /** - * Elasticsearch 6x test connection block - * @codeCoverageIgnore + * Elasticsearch 6.x test connection block */ class TestConnection extends \Magento\AdvancedSearch\Block\Adminhtml\System\Config\TestConnection { 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 13fee19dd5924..0450ae1330cd8 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 @@ -208,4 +208,4 @@ Magento/InventoryGroupedProductIndexer/Indexer Magento/Customer/Model/FileUploaderDataResolver.php Magento/Customer/Model/Customer/DataProvider.php Magento/InventoryShippingAdminUi/Ui/DataProvider -Magento/Elasticsearch6/Model/Client/Elasticsearch.php +Magento/Elasticsearch6/Model/Client From 96268150bc9c2b065d37f4c8576f94e4d45723cb Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Fri, 1 Mar 2019 11:04:44 -0600 Subject: [PATCH 251/254] magento/magento2#21458: Elasticsearch 6.x support - updated composer dependencies --- composer.lock | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/composer.lock b/composer.lock index 48aef8b603d4a..a880a6dea16d1 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": "f22c780b1ed27ba951c0562e20078a70", + "content-hash": "19b030406adc1028eb58a37e975c5f74", "packages": [ { "name": "braintree/braintree_php", @@ -535,29 +535,31 @@ }, { "name": "elasticsearch/elasticsearch", - "version": "v5.4.0", + "version": "v6.1.0", "source": { "type": "git", "url": "https://github.com/elastic/elasticsearch-php.git", - "reference": "d3c5b55ad94f5053ca76c48585b4cde2cdc6bc59" + "reference": "b237a37b2cdf23a5a17fd3576cdea771394ad00d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/d3c5b55ad94f5053ca76c48585b4cde2cdc6bc59", - "reference": "d3c5b55ad94f5053ca76c48585b4cde2cdc6bc59", + "url": "https://api.github.com/repos/elastic/elasticsearch-php/zipball/b237a37b2cdf23a5a17fd3576cdea771394ad00d", + "reference": "b237a37b2cdf23a5a17fd3576cdea771394ad00d", "shasum": "" }, "require": { + "ext-json": ">=1.3.7", "guzzlehttp/ringphp": "~1.0", - "php": "^5.6|^7.0", + "php": "^7.0", "psr/log": "~1.0" }, "require-dev": { "cpliakas/git-wrapper": "~1.0", "doctrine/inflector": "^1.1", "mockery/mockery": "0.9.4", - "phpunit/phpunit": "^4.7|^5.4", - "sami/sami": "~3.2", + "phpstan/phpstan-shim": "0.8.3", + "phpunit/phpunit": "6.3.0", + "squizlabs/php_codesniffer": "3.0.2", "symfony/finder": "^2.8", "symfony/yaml": "^2.8" }, @@ -586,7 +588,7 @@ "elasticsearch", "search" ], - "time": "2019-01-08T18:57:00+00:00" + "time": "2019-01-08T18:53:46+00:00" }, { "name": "guzzlehttp/ringphp", @@ -7813,6 +7815,7 @@ "mock", "xunit" ], + "abandoned": true, "time": "2018-08-09T05:50:03+00:00" }, { From fa890bed53c33eecf182b90c68605af517f55dd9 Mon Sep 17 00:00:00 2001 From: Stepan Furman <furman.stepan@gmail.com> Date: Wed, 27 Feb 2019 22:45:35 +0200 Subject: [PATCH 252/254] MSI-1988 issue fixed: Database Rollback not working M2.3 --- app/code/Magento/Backup/Model/Db.php | 24 +++- .../ResourceModel/Table/GetListTables.php | 42 +++++++ .../Model/ResourceModel/View/GetListViews.php | 41 +++++++ .../ResourceModel/View/GetViewsBackup.php | 106 ++++++++++++++++++ 4 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php create mode 100644 app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php create mode 100644 app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php diff --git a/app/code/Magento/Backup/Model/Db.php b/app/code/Magento/Backup/Model/Db.php index bc458a0a8e4bf..a385a476845db 100644 --- a/app/code/Magento/Backup/Model/Db.php +++ b/app/code/Magento/Backup/Model/Db.php @@ -6,6 +6,8 @@ namespace Magento\Backup\Model; use Magento\Backup\Helper\Data as Helper; +use Magento\Backup\Model\ResourceModel\Table\GetListTables; +use Magento\Backup\Model\ResourceModel\View\GetViewsBackup; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\RuntimeException; @@ -43,19 +45,35 @@ class Db implements \Magento\Framework\Backup\Db\BackupDbInterface */ private $helper; + /** + * @var GetListTables + */ + private $getListTables; + + /** + * @var GetViewsBackup + */ + private $getViewsBackup; + /** * @param \Magento\Backup\Model\ResourceModel\Db $resourceDb * @param \Magento\Framework\App\ResourceConnection $resource + * @param GetListTables|null $getListTables + * @param GetViewsBackup|null $getViewsBackup * @param Helper|null $helper */ public function __construct( \Magento\Backup\Model\ResourceModel\Db $resourceDb, \Magento\Framework\App\ResourceConnection $resource, - ?Helper $helper = null + ?Helper $helper = null, + GetListTables $getListTables = null, + GetViewsBackup $getViewsBackup = null ) { $this->_resourceDb = $resourceDb; $this->_resource = $resource; $this->helper = $helper ?? ObjectManager::getInstance()->get(Helper::class); + $this->getListTables = $getListTables ?: ObjectManager::getInstance()->get(GetListTables::class); + $this->getViewsBackup = $getViewsBackup ?: ObjectManager::getInstance()->get(GetViewsBackup::class); } /** @@ -161,7 +179,7 @@ public function createBackup(\Magento\Framework\Backup\Db\BackupInterface $backu $this->getResource()->beginTransaction(); - $tables = $this->getResource()->getTables(); + $tables = $this->getListTables->execute(); $backup->write($this->getResource()->getHeader()); @@ -198,6 +216,8 @@ public function createBackup(\Magento\Framework\Backup\Db\BackupInterface $backu $backup->write($this->getResource()->getTableDataAfterSql($table)); } } + $this->getViewsBackup->execute($backup); + $backup->write($this->getResource()->getTableForeignKeysSql()); $backup->write($this->getResource()->getTableTriggersSql()); $backup->write($this->getResource()->getFooter()); diff --git a/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php new file mode 100644 index 0000000000000..5a9f96a50c35a --- /dev/null +++ b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backup\Model\ResourceModel\Table; + +use Magento\Framework\App\ResourceConnection; + +/** + * Class GetListTables + */ +class GetListTables +{ + private const TABLE_TYPE = 'BASE TABLE'; + + /** + * @var ResourceConnection + */ + private $resource; + + /** + * @param ResourceConnection $resource + */ + public function __construct(ResourceConnection $resource) + { + $this->resource = $resource; + } + + /** + * @return array + */ + public function execute() + { + return $this->resource->getConnection('backup')->fetchCol( +"SHOW FULL TABLES WHERE `Table_type` = ?", + self::TABLE_TYPE + ); + } +} diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php new file mode 100644 index 0000000000000..54f2a1a65d280 --- /dev/null +++ b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backup\Model\ResourceModel\View; + +use Magento\Framework\App\ResourceConnection; + +/** + * Class GetListViews + */ +class GetListViews +{ + private const TABLE_TYPE = 'VIEW'; + /** + * @var ResourceConnection + */ + private $resource; + + /** + * @param ResourceConnection $resource + */ + public function __construct(ResourceConnection $resource) + { + $this->resource = $resource; + } + + /** + * @return array + */ + public function execute() + { + return $this->resource->getConnection('backup')->fetchCol( + "SHOW FULL TABLES WHERE `Table_type` = ?", + self::TABLE_TYPE + ); + } +} diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php b/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php new file mode 100644 index 0000000000000..bda5ff58c0594 --- /dev/null +++ b/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Backup\Model\ResourceModel\View; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Backup\Db\BackupInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; + +/** + * Class GetViewsBackup + */ +class GetViewsBackup +{ + /** + * @var GetListViews + */ + private $getListViews; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @var AdapterInterface + */ + private $connection; + + /** + * @param GetListViews $getListViews + * @param ResourceConnection $resourceConnection + */ + public function __construct( + GetListViews $getListViews, + ResourceConnection $resourceConnection + ) { + $this->getListViews = $getListViews; + $this->resourceConnection = $resourceConnection; + } + + /** + * @param BackupInterface $backup + */ + public function execute(BackupInterface $backup) + { + $views = $this->getListViews->execute(); + + foreach ($views as $view) { + $backup->write($this->getViewHeader($view)); + $backup->write($this->getDropViewSql($view)); + $backup->write($this->getShowCreateView($view)); + } + } + + /** + * @return AdapterInterface + */ + private function getConnection() + { + if (!$this->connection) { + $this->connection = $this->resourceConnection->getConnection('backup'); + } + + return $this->connection; + } + + /** + * @param string $viewName + * @return string + */ + private function getShowCreateView($viewName) + { + $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); + $query = 'SHOW CREATE VIEW ' . $quotedViewName; + $row = $this->getConnection()->fetchRow($query); + $regExp = '/\sDEFINER\=\`([^`]*)\`\@\`([^`]*)\`/'; + $sql = preg_replace($regExp, '', $row['Create View']); + + return $sql . ';' . "\n"; + } + + /** + * @param string $viewName + * @return string + */ + public function getViewHeader($viewName) + { + $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); + return "\n--\n" . "-- Structure for view {$quotedViewName}\n" . "--\n\n"; + } + + /** + * @param string $viewName + * @return string + */ + public function getDropViewSql($viewName) + { + $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); + return sprintf('DROP VIEW IF EXISTS %s;' . "\n", $quotedViewName); + } +} From 7e45f62f7af0e4af839b66f412402b5a5d84a620 Mon Sep 17 00:00:00 2001 From: Ievgen Shakhsuvarov <ishakhsuvarov@magento.com> Date: Thu, 28 Feb 2019 21:17:59 -0600 Subject: [PATCH 253/254] magento-engcom/magento2ce#2639: Minor coding practices improvements --- app/code/Magento/Backup/Model/Db.php | 21 +++++++------- .../ResourceModel/Table/GetListTables.php | 8 ++++-- ...tViewsBackup.php => CreateViewsBackup.php} | 28 +++++++++++++------ .../Model/ResourceModel/View/GetListViews.php | 7 +++-- 4 files changed, 40 insertions(+), 24 deletions(-) rename app/code/Magento/Backup/Model/ResourceModel/View/{GetViewsBackup.php => CreateViewsBackup.php} (74%) diff --git a/app/code/Magento/Backup/Model/Db.php b/app/code/Magento/Backup/Model/Db.php index a385a476845db..084b35448a823 100644 --- a/app/code/Magento/Backup/Model/Db.php +++ b/app/code/Magento/Backup/Model/Db.php @@ -7,7 +7,7 @@ use Magento\Backup\Helper\Data as Helper; use Magento\Backup\Model\ResourceModel\Table\GetListTables; -use Magento\Backup\Model\ResourceModel\View\GetViewsBackup; +use Magento\Backup\Model\ResourceModel\View\CreateViewsBackup; use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\RuntimeException; @@ -51,29 +51,30 @@ class Db implements \Magento\Framework\Backup\Db\BackupDbInterface private $getListTables; /** - * @var GetViewsBackup + * @var CreateViewsBackup */ private $getViewsBackup; /** - * @param \Magento\Backup\Model\ResourceModel\Db $resourceDb + * Db constructor. + * @param ResourceModel\Db $resourceDb * @param \Magento\Framework\App\ResourceConnection $resource - * @param GetListTables|null $getListTables - * @param GetViewsBackup|null $getViewsBackup * @param Helper|null $helper + * @param GetListTables|null $getListTables + * @param CreateViewsBackup|null $getViewsBackup */ public function __construct( - \Magento\Backup\Model\ResourceModel\Db $resourceDb, + ResourceModel\Db $resourceDb, \Magento\Framework\App\ResourceConnection $resource, ?Helper $helper = null, - GetListTables $getListTables = null, - GetViewsBackup $getViewsBackup = null + ?GetListTables $getListTables = null, + ?CreateViewsBackup $getViewsBackup = null ) { $this->_resourceDb = $resourceDb; $this->_resource = $resource; $this->helper = $helper ?? ObjectManager::getInstance()->get(Helper::class); - $this->getListTables = $getListTables ?: ObjectManager::getInstance()->get(GetListTables::class); - $this->getViewsBackup = $getViewsBackup ?: ObjectManager::getInstance()->get(GetViewsBackup::class); + $this->getListTables = $getListTables ?? ObjectManager::getInstance()->get(GetListTables::class); + $this->getViewsBackup = $getViewsBackup ?? ObjectManager::getInstance()->get(CreateViewsBackup::class); } /** diff --git a/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php index 5a9f96a50c35a..73c4221feba3f 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php +++ b/app/code/Magento/Backup/Model/ResourceModel/Table/GetListTables.php @@ -10,7 +10,7 @@ use Magento\Framework\App\ResourceConnection; /** - * Class GetListTables + * Provides full list of tables in the database. This list excludes views, to allow different backup process. */ class GetListTables { @@ -30,12 +30,14 @@ public function __construct(ResourceConnection $resource) } /** + * Get list of database tables excluding views. + * * @return array */ - public function execute() + public function execute(): array { return $this->resource->getConnection('backup')->fetchCol( -"SHOW FULL TABLES WHERE `Table_type` = ?", + "SHOW FULL TABLES WHERE `Table_type` = ?", self::TABLE_TYPE ); } diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php b/app/code/Magento/Backup/Model/ResourceModel/View/CreateViewsBackup.php similarity index 74% rename from app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php rename to app/code/Magento/Backup/Model/ResourceModel/View/CreateViewsBackup.php index bda5ff58c0594..51b49dcb9e48a 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/View/GetViewsBackup.php +++ b/app/code/Magento/Backup/Model/ResourceModel/View/CreateViewsBackup.php @@ -12,9 +12,9 @@ use Magento\Framework\DB\Adapter\AdapterInterface; /** - * Class GetViewsBackup + * Creates backup of Views in the database. */ -class GetViewsBackup +class CreateViewsBackup { /** * @var GetListViews @@ -44,23 +44,27 @@ public function __construct( } /** + * Write backup data to backup file. + * * @param BackupInterface $backup */ - public function execute(BackupInterface $backup) + public function execute(BackupInterface $backup): void { $views = $this->getListViews->execute(); foreach ($views as $view) { $backup->write($this->getViewHeader($view)); $backup->write($this->getDropViewSql($view)); - $backup->write($this->getShowCreateView($view)); + $backup->write($this->getCreateView($view)); } } /** + * Retrieve Database connection for Backup. + * * @return AdapterInterface */ - private function getConnection() + private function getConnection(): AdapterInterface { if (!$this->connection) { $this->connection = $this->resourceConnection->getConnection('backup'); @@ -70,10 +74,12 @@ private function getConnection() } /** + * Get CREATE VIEW query for the specific view. + * * @param string $viewName * @return string */ - private function getShowCreateView($viewName) + private function getCreateView(string $viewName): string { $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); $query = 'SHOW CREATE VIEW ' . $quotedViewName; @@ -85,22 +91,26 @@ private function getShowCreateView($viewName) } /** + * Prepare a header for View being dumped. + * * @param string $viewName * @return string */ - public function getViewHeader($viewName) + public function getViewHeader(string $viewName): string { $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); return "\n--\n" . "-- Structure for view {$quotedViewName}\n" . "--\n\n"; } /** + * Make sure that View being created is deleted if already exists. + * * @param string $viewName * @return string */ - public function getDropViewSql($viewName) + public function getDropViewSql(string $viewName): string { $quotedViewName = $this->getConnection()->quoteIdentifier($viewName); - return sprintf('DROP VIEW IF EXISTS %s;' . "\n", $quotedViewName); + return sprintf('DROP VIEW IF EXISTS %s;\n', $quotedViewName); } } diff --git a/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php index 54f2a1a65d280..c76ea2842180b 100644 --- a/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php +++ b/app/code/Magento/Backup/Model/ResourceModel/View/GetListViews.php @@ -10,11 +10,12 @@ use Magento\Framework\App\ResourceConnection; /** - * Class GetListViews + * Get list of database views. */ class GetListViews { private const TABLE_TYPE = 'VIEW'; + /** * @var ResourceConnection */ @@ -29,9 +30,11 @@ public function __construct(ResourceConnection $resource) } /** + * Get list of database views. + * * @return array */ - public function execute() + public function execute(): array { return $this->resource->getConnection('backup')->fetchCol( "SHOW FULL TABLES WHERE `Table_type` = ?", From 03f0c3aa6c5e9bc2207c197b54e89ae727e15342 Mon Sep 17 00:00:00 2001 From: nmalevanec <mikola.malevanec@transoftgroup.com> Date: Mon, 4 Mar 2019 15:31:18 +0200 Subject: [PATCH 254/254] Fix static tests. --- .../Product/Form/Modifier/Links.php | 32 +++++++++++++++---- .../Product/Form/Modifier/Samples.php | 22 +++++++++---- 2 files changed, 41 insertions(+), 13 deletions(-) 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 e4889b160963f..9ab664cb27839 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 @@ -3,22 +3,24 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier; -use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Downloadable\Model\Product\Type; -use Magento\Downloadable\Model\Source\TypeUpload; use Magento\Downloadable\Model\Source\Shareable; -use Magento\Store\Model\StoreManagerInterface; +use Magento\Downloadable\Model\Source\TypeUpload; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Ui\Component\DynamicRows; use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\Container; +use Magento\Ui\Component\DynamicRows; use Magento\Ui\Component\Form; /** - * Class adds a grid with links + * Class adds a grid with links. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Links extends AbstractModifier @@ -86,7 +88,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -101,7 +103,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function modifyMeta(array $meta) @@ -160,6 +162,8 @@ public function modifyMeta(array $meta) } /** + * Get dynamic rows meta. + * * @return array */ protected function getDynamicRows() @@ -180,6 +184,8 @@ protected function getDynamicRows() } /** + * Get single link record meta. + * * @return array */ protected function getRecord() @@ -221,6 +227,8 @@ protected function getRecord() } /** + * Get link title meta. + * * @return array */ protected function getTitleColumn() @@ -248,6 +256,8 @@ protected function getTitleColumn() } /** + * Get link price meta. + * * @return array */ protected function getPriceColumn() @@ -283,6 +293,8 @@ protected function getPriceColumn() } /** + * Get link file element meta. + * * @return array */ protected function getFileColumn() @@ -347,6 +359,8 @@ protected function getFileColumn() } /** + * Get sample container meta. + * * @return array */ protected function getSampleColumn() @@ -407,6 +421,8 @@ protected function getSampleColumn() } /** + * Get link "is sharable" element meta. + * * @return array */ protected function getShareableColumn() @@ -425,6 +441,8 @@ protected function getShareableColumn() } /** + * Get link "max downloads" element meta. + * * @return array */ protected function getMaxDownloadsColumn() 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 b12c415b0b0b1..3890ee5b9e2b2 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 @@ -3,21 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Downloadable\Ui\DataProvider\Product\Form\Modifier; -use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Downloadable\Model\Product\Type; use Magento\Downloadable\Model\Source\TypeUpload; -use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Stdlib\ArrayManager; -use Magento\Ui\Component\DynamicRows; use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\Container; +use Magento\Ui\Component\DynamicRows; use Magento\Ui\Component\Form; /** - * Class adds a grid with samples + * Class adds a grid with samples. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Samples extends AbstractModifier @@ -77,7 +79,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function modifyData(array $data) { @@ -90,7 +92,7 @@ public function modifyData(array $data) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function modifyMeta(array $meta) @@ -135,6 +137,8 @@ public function modifyMeta(array $meta) } /** + * Get sample rows meta. + * * @return array */ protected function getDynamicRows() @@ -155,6 +159,8 @@ protected function getDynamicRows() } /** + * Get single sample row meta. + * * @return array */ protected function getRecord() @@ -192,6 +198,8 @@ protected function getRecord() } /** + * Get sample title meta. + * * @return array */ protected function getTitleColumn() @@ -219,6 +227,8 @@ protected function getTitleColumn() } /** + * Get sample element meta. + * * @return array */ protected function getSampleColumn()